What are the differences between references and pointers?
Both references and pointers can be used to change local variables of one function inside another function. Both of them can also be used to save copying of big objects when passed as arguments to functions or returned from functions, to get efficiency gain. Despite above similarities, there are following differences between references and pointers.
References are less powerful than pointers
- Once a reference is created, it cannot be later made to reference another object; it cannot be reseated. This is often done with pointers.
- References cannot be NULL. Pointers are often made NULL to indicate that they are not pointing to any valid thing.
- A reference must be initialized when declared. There is no such restriction with pointers
// Comment to end of line
/* Multi-line comment */
#include <stdio.h> // Insert standard header file
#include "myfile.h" // Insert file in current directoryint x; // Declare x to be an integer (value undefined)
int x=255; // Declare and initialize x to 255
short s; long l; // Usually 16 or 32 bit integer (int may be either)
char c='a'; // Usually 8 bit character
unsigned char u=255;
signed char s=-1; // char might be either
unsigned long x =
0xffffffffL; // short, int, long are signed
float f; double d; // Single or double precision real (never unsigned)
bool b=true; // true or false, may also use int (1 or 0)
int a, b, c; // Multiple declarations
int a[10]; // Array of 10 ints (a[0] through a[9])
int a[]={0,1,2}; // Initialized array (or a[3]={0,1,2}; )
int a[2][2]={{1,2},{4,5}}; // Array of array of ints
char s[]="hello"; // String (6 elements including '\0')
std::string s = "Hello" // Creates string object with value "Hello"
std::string s = R"(Hello
World)"; // Creates string object with value "Hello\nWorld"
int* p; // p is a pointer to (address of) int
char* s="hello"; // s points to unnamed array containing "hello"
void* p=nullptr; // Address of untyped memory (nullptr is 0)
int& r=x; // r is a reference to (alias of) int x
enum weekend {SAT,SUN}; // weekend is a type with values SAT and SUN
enum weekend day; // day is a variable of type weekend
enum weekend{SAT=0,SUN=1}; // Explicit representation as int
enum {SAT,SUN} day; // Anonymous enum
enum class Color {Red,Blue};// Color is a strict type with values Red and Blue
Color x = Color::Red; // Assign Color x to red
typedef String char*; // String s; means char* s;
const int c=3; // Constants must be initialized, cannot assign to
const int* p=a; // Contents of p (elements of a) are constant
int* const p=a; // p (but not contents) are constant
const int* const p=a; // Both p and its contents are constant
const int& cr=x; // cr cannot be assigned to change xx=y; // Every expression is a statement
int x; // Declarations are statements
; // Empty statement
{ // A block is a single statement
int x; // Scope of x is from declaration to end of block
}
if (x) a; // If x is true (not 0), evaluate a
else if (y) b; // If not x and y (optional, may be repeated)
else c; // If not x and not y (optional)
while (x) a; // Repeat 0 or more times while x is true
for (x; y; z) a; // Equivalent to: x; while(y) {a; z;}
for (x : y) a; // Range-based for loop e.g.
// for (auto& x in someList) x.y();
do a; while (x); // Equivalent to: a; while(x) a;
switch (x) { // x must be int
case X1: a; // If x == X1 (must be a const), jump here
case X2: b; // Else if x == X2, jump here
default: c; // Else jump here (optional)
}
break; // Jump out of while, do, or for loop, or switch
continue; // Jump to bottom of while, do, or for loop
return x; // Return x from function to caller
try { a; }
catch (T t) { b; } // If a throws a T, then jump here
catch (...) { c; } // If a throws something else, jump hereint f(int x, int y); // f is a function taking 2 ints and returning int
void f(); // f is a procedure taking no arguments
void f(int a=0); // f() is equivalent to f(0)
f(); // Default return type is int
inline f(); // Optimize for speed
f() { statements; } // Function definition (must be global)
T operator+(T x, T y); // a+b (if type T) calls operator+(a, b)
T operator-(T x); // -a calls function operator-(a)
T operator++(int); // postfix ++ or -- (parameter ignored)
extern "C" {void f();} // f() was compiled in CFunction parameters and return values may be of any type. A function must either be declared or defined before it is used. It may be declared first and defined later. Every program consists of a set of a set of global variable declarations and a set of function definitions (possibly in separate files), one of which must be:
int main() { statements... } // or
int main(int argc, char* argv[]) { statements... }argv is an array of argc strings from the command line.
By convention, main returns status 0 if successful, 1 or higher for errors.
Functions with different parameters may have the same name (overloading). Operators except :: . .* ?: may be overloaded.
Precedence order is not affected. New operators may not be created.
Operators are grouped by precedence, highest first. Unary operators and assignment evaluate right to left. All others are left to right. Precedence does not affect order of evaluation, which is undefined. There are no run time checks for arrays out of bounds, invalid pointers, etc.
T::X // Name X defined in class T
N::X // Name X defined in namespace N
::X // Global name X
t.x // Member x of struct or class t
p-> x // Member x of struct or class pointed to by p
a[i] // i'th element of array a
f(x,y) // Call to function f with arguments x and y
T(x,y) // Object of class T initialized with x and y
x++ // Add 1 to x, evaluates to original x (postfix)
x-- // Subtract 1 from x, evaluates to original x
typeid(x) // Type of x
typeid(T) // Equals typeid(x) if x is a T
dynamic_cast< T>(x) // Converts x to a T, checked at run time.
static_cast< T>(x) // Converts x to a T, not checked
reinterpret_cast< T>(x) // Interpret bits of x as a T
const_cast< T>(x) // Converts x to same type T but not const
sizeof x // Number of bytes used to represent object x
sizeof(T) // Number of bytes to represent type T
++x // Add 1 to x, evaluates to new value (prefix)
--x // Subtract 1 from x, evaluates to new value
~x // Bitwise complement of x
!x // true if x is 0, else false (1 or 0 in C)
-x // Unary minus
+x // Unary plus (default)
&x // Address of x
*p // Contents of address p (*&x equals x)
new T // Address of newly allocated T object
new T(x, y) // Address of a T initialized with x, y
new T[x] // Address of allocated n-element array of T
delete p // Destroy and free object at address p
delete[] p // Destroy and free array of objects at p
(T) x // Convert x to T (obsolete, use .._cast<T>(x))
x << y // x shifted y bits to left (x * pow(2, y))
x >> y // x shifted y bits to right (x / pow(2, y))
x & y // Bitwise and (3 & 6 is 2)
x ^ y // Bitwise exclusive or (3 ^ 6 is 5)
x | y // Bitwise or (3 | 6 is 7)
x && y // x and then y (evaluates y only if x (not 0))
x || y // x or else y (evaluates y only if x is false (0))
x = y // Assign y to x, returns new value of x
x += y // x = x + y, also -= *= /= <<= >>= &= |= ^=
// THIS IS A TERNARY IF STATEMENT BELOW
x ? y : z // y if x is true (nonzero), else z
throw x // Throw exception, aborts if not caught
x , y // evaluates x and y, returns y (seldom used)Idk if you'll be asked to do anything with classes, it depends on what they want you to do in the job, but most likely not asked in an interview.
class T { // A new type
private: // Section accessible only to T's member functions
protected: // Also accessible to classes derived from T
public: // Accessible to all
int x; // Member data
void f(); // Member function
void g() {return;} // Inline member function
void h() const; // Does not modify any data members
int operator+(int y); // t+y means t.operator+(y)
int operator-(); // -t means t.operator-()
T(): x(1) {} // Constructor with initialization list
T(const T& t): x(t.x) {}// Copy constructor
T& operator=(const T& t)
{x=t.x; return *this; } // Assignment operator
~T(); // Destructor (automatic cleanup routine)
explicit T(int a); // Allow t=T(3) but not t=3
T(float x): T((int)x) {}// Delegate constructor to T(int)
operator int() const
{return x;} // Allows int(t)
friend void i(); // Global function i() has private access
friend class U; // Members of class U have private access
static int y; // Data shared by all T objects
static void l(); // Shared code. May access y but not x
class Z {}; // Nested class T::Z
typedef int V; // T::V means int
};
void T::f() { // Code for member function f of class T
this->x = x;} // this is address of self (means x=x;)
int T::y = 2; // Initialization of static member (required)
T::l(); // Call to static member
T t; // Create object t implicit call constructor
t.f(); // Call method f on object t
struct T { // Equivalent to: class T { public:
virtual void i(); // May be overridden at run time by derived class
virtual void g()=0; }; // Must be overridden (pure virtual)
class U: public T { // Derived class U inherits all members of base T
public:
void g(int) override; }; // Override method g
class V: private T {}; // Inherited members of T become private
class W: public T, public U {};
// Multiple inheritance
class X: public virtual T {};
// Classes derived from X have base T directlyAll classes have a default copy constructor, assignment operator, and destructor, which perform the corresponding operations on each data member and each base class as shown above. There is also a default no-argument constructor (required to create arrays) if the class has no constructors. Constructors, assignment, and destructors do not inherit.
Again with namespaces and templates, probably not going to be used in an interview, but to be safe you should look at them a lil bit
template <class T> T f(T t);// Overload f for all types
template <class T> class X {// Class with type parameter T
X(T t); }; // A constructor
template <class T> X<T>::X(T t) {}
// Definition of constructor
X<int> x(3); // An object of type "X of int"
template <class T, class U=T, int n=0>
// Template with default parametersnamespace N {class T {};} // Hide name T
N::T t; // Use name T in namespace N
using namespace N; // Make T visible without N::Super important all of this, I don't even use half of it normally for personal projects, but I'm sure they'd want the knowledge for an internship or job
#include <memory> // Include memory (std namespace)
shared_ptr<int> x; // Empty shared_ptr to a integer on heap. Uses reference counting for cleaning up objects.
x = make_shared<int>(12); // Allocate value 12 on heap
shared_ptr<int> y = x; // Copy shared_ptr, implicit changes reference count to 2.
cout << *y; // Dereference y to print '12'
if (y.get() == x.get()) { // Raw pointers (here x == y)
cout << "Same";
}
y.reset(); // Eliminate one owner of object
if (y.get() != x.get()) {
cout << "Different";
}
if (y == nullptr) { // Can compare against nullptr (here returns true)
cout << "Empty";
}
y = make_shared<int>(15); // Assign new value
cout << *y; // Dereference x to print '15'
cout << *x; // Dereference x to print '12'
weak_ptr<int> w; // Create empty weak pointer
w = y; // w has weak reference to y.
if (shared_ptr<int> s = w.lock()) { // Has to be copied into a shared_ptr before usage
cout << *s;
}
unique_ptr<int> z; // Create empty unique pointers
unique_ptr<int> q;
z = make_unique<int>(16); // Allocate int (16) on heap. Only one reference allowed.
q = move(z); // Move reference from z to q.
if (z == nullptr){
cout << "Z null";
}
cout << *q;
shared_ptr<B> r;
r = dynamic_pointer_cast<B>(t); // Converts t to a shared_ptr<B>
#include <iostream> // Include iostream (std namespace)
cin >> x >> y; // Read words x and y (any type) from stdin
cout << "x=" << 3 << endl; // Write line to stdout
cerr << x << y << flush; // Write to stderr and flush
c = cin.get(); // c = getchar();
cin.get(c); // Read char
cin.getline(s, n, '\n'); // Read line into char s[n] to '\n' (default)
if (cin) // Good state (not EOF)?
// To read/write any type T:
istream& operator>>(istream& i, T& x) {i >> ...; x=...; return i;}
ostream& operator<<(ostream& o, const T& x) {return o << ...;}#include <string> // Include string (std namespace)
string s1, s2="hello"; // Create strings
s1.size(), s2.size(); // Number of characters: 0, 5
s1 += s2 + ' ' + "world"; // Concatenation
s1 == "hello world" // Comparison, also <, >, !=, etc.
s1[0]; // 'h'
s1.substr(m, n); // Substring of size n starting at s1[m]
s1.c_str(); // Convert to const char*
s1 = to_string(12.05); // Converts number to string
getline(cin, s); // Read line ending in '\n'#include <vector> // Include vector (std namespace)
vector<int> a(10); // a[0]..a[9] are int (default size is 0)
vector<int> b{1,2,3}; // Create vector with values 1,2,3
a.size(); // Number of elements (10)
a.push_back(3); // Increase size to 11, a[10]=3
a.back()=4; // a[10]=4;
a.pop_back(); // Decrease size by 1
a.front(); // a[0];
a[20]=1; // Crash: not bounds checked
a.at(20)=1; // Like a[20] but throws out_of_range()
for (int& p : a)
p=0; // C++11: Set all elements of a to 0
for (vector<int>::iterator p=a.begin(); p!=a.end(); ++p)
*p=0; // C++03: Set all elements of a to 0
vector<int> b(a.begin(), a.end()); // b is copy of a
vector<T> c(n, x); // c[0]..c[n-1] init to x
T d[10]; vector<T> e(d, d+10); // e is initialized from ddeque<T> is like vector<T>, but also supports:
#include <deque> // Include deque (std namespace)
a.push_front(x); // Puts x at a[0], shifts elements toward back
a.pop_front(); // Removes a[0], shifts toward frontmap (associative array - usually implemented as binary search trees - avg. time complexity: O(log n))
#include <map> // Include map (std namespace)
map<string, int> a; // Map from string to int
a["hello"] = 3; // Add or replace element a["hello"]
for (auto& p:a)
cout << p.first << p.second; // Prints hello, 3
a.size(); // 1#include <unordered_map> // Include map (std namespace)
unordered_map<string, int> a; // Map from string to int
a["hello"] = 3; // Add or replace element a["hello"]
for (auto& p:a)
cout << p.first << p.second; // Prints hello, 3
a.size(); // 1set (store unique elements - usually implemented as binary search trees - avg. time complexity: O(log n))
#include <set> // Include set (std namespace)
set<int> s; // Set of integers
s.insert(123); // Add element to set
if (s.find(123) != s.end()) // Search for an element
s.erase(123);
cout << s.size(); // Number of elements in setunordered_set (store unique elements - usually implemented as a hash set - avg. time complexity: O(1))
#include <unordered_set> // Include set (std namespace)
unordered_set<int> s; // Set of integers
s.insert(123); // Add element to set
if (s.find(123) != s.end()) // Search for an element
s.erase(123);
cout << s.size(); // Number of elements in set#include <algorithm> // Include algorithm (std namespace)
min(x, y); max(x, y); // Smaller/larger of x, y (any type defining <)
swap(x, y); // Exchange values of variables x and y
sort(a, a+n); // Sort array a[0]..a[n-1] by <
sort(a.begin(), a.end()); // Sort vector or deque
reverse(a.begin(), a.end()); // Reverse vector or dequeUsing smart pointers, we can make pointers to work in way that we don’t need to explicitly call delete. Smart pointer is a wrapper class over a pointer with operator like * and -> overloaded. The objects of smart pointer class look like pointer, but can do many things that a normal pointer can’t like automatic destruction (yes, we don’t have to explicitly use delete), reference counting and more. The idea is to make a class with a pointer, destructor and overloaded operators like * and ->. Since destructor is automatically called when an object goes out of scope, the dynamically allocated memory would automatically deleted (or reference count can be decremented). Consider the following simple smartPtr class.
// Smart pointers can be generic too, see https://www.geeksforgeeks.org/smart-pointers-cpp/ if you can't figure out how
class SmartPtr {
int *ptr; // Actual pointer
public:
explicit SmartPtr(int *p = NULL) { ptr = p; }
// Destructor
~SmartPtr() { delete(ptr); }
// Overloading dereferencing operator
int &operator *() { return *ptr; }
};
int main() {
SmartPtr ptr(new int());
*ptr = 20;
cout << *ptr;
// We don't need to call delete ptr: when the object
// ptr goes out of scope, destructor for it is automatically
// called and destructor does delete ptr.
return 0;
}#include <iostream>
#include <cstdlib>
#include <pthread.h>
using namespace std;
#define NUM_THREADS 5 // Define the number of threads we want to use
void *PrintHello(void *threadid) { // Our multhreaded function to use
long tid;
tid = (long)threadid;
cout << "Hello World! Thread ID, " << tid << endl;
pthread_exit(NULL);
}
int main () {
pthread_t threads[NUM_THREADS]; // Create a pthread [POSIX thread]
int rc; // Number of thread if we can't create it to print out
int i; // Number of thread if we can create it to print out
for( i = 0; i < NUM_THREADS; i++ ) { // Make 5 threads
cout << "main() : creating thread, " << i << endl;
rc = pthread_create(&threads[i], NULL, PrintHello, (void *)i); // Creating a thread with our function
if (rc) { // If we were unable to create it
cout << "Error:unable to create thread," << rc << endl;
exit(-1);
}
}
pthread_exit(NULL); // You must exit
}I really don't know a whole lot about data races in C++ specifically, but
Website explaining race conditions vs data races
I was asked about keeping threads safe from info being accessed from them (to prevent data races and such)