C++ Fundamentals
What is the problem with wrapping this in a shared_ptr, and how do you get around it?
If you wrap this in a shared pointer, then both the a member function and the client code could have shared_ptr's which are independently 'owning' the object (i.e. they have different manager objects). This will almost inevitably lead to double deletions. One can extended the templated class enable_shared_from_this<T>, which will give your class a member function shared_from_this() which will be a shared_ptr which has the same manager object as the shared_ptr's being used by the client code (assuming that this was constructed inside a call to the shared_ptr constructor). This is implemented by storing a weak_ptr in this, which is initialized when the first shared_ptr is initialized at construction.
How does one prevent a default copy constructor/move constructor/assignment operator from being created?
=delete()
What is a copy constructor and how do you use it? In what scenarios could it be invoked? When will it not be invoked?
A copy constructor is a function which initializes an object using the contents of an object of the same type. Its signature should be: MyType(const MyType & other); It could be invoked in any of the following scenarios: 1) When an object is declared and assigned to another object at the same time: Object b = a 2) When an object is passed by value to a function 2.5) When an object is returned by value 3) When an exception is thrown 4) When an exception is caught 5) When an object is placed in a brace-enclosed initializer list It won't be invoked for regular assignment: T a; T b; a = b; In that case the assignment operator will be invoked.
What is a unique_ptr? How do they differ from a shared_ptr? How can you construct them? How do you transfer ownership?
A unique_ptr manages an object allocated on the heap (i.e. the destructor of the unique_ptr frees the object). Only one unique_ptr can manage any given object. There should be no other raw or smart pointers for that object. This eliminates the need to allocate a manager object, and makes unique_ptr's fast. Construct a unique_ptr: unique_ptr<Thing> local_ptr(new Thing); or unique_ptr<Thing> local_ptr(make_unique<Thing>()); A unique pointer does not have a copy constructor or a copy assignment operator (so the right hand side of an assignment can NOT be an lvalue). However it does have a move constructor and move assignment operator which transfer ownership from the rvalue to the object on the left of the assignment.
What is an rvalue reference?
An an rvalue reference is a reference to an rvalue (i.e. a value which can not be assigned to/is not bound to an identifier). To declare a variable foo which is an rvalue reference to an object of type T: T&& foo
What is ADL and what does it stand for?
ADL stands for argument-dependent lookup. It is a means by which C++ looks for unqualified function names. In addition to examining the namespaces that would be examined in other unqualified lookups, the namespaces of the function's arguments are examined.
What is the explicit keyword used for?
By default a single argument constructor is used by the compiler to implicitly convert objects of the argument type to the type of the object being constructed. The explicit keyword prevents this, and requires an explicit cast.
What is the mutable keyword and when should it be used?
It marks a member of a class which can mutate even in a const instance of that class. It should only be used when a semantically const function needs to modify the member.
What are move semantics? How do they relate to copy constructors and assignment operators (give function signatures)?
Move semantics are scenarios where an assignment (either in initialization or explicit assignment) causes the resources owned by the object on the right hand side (which is an rvalue/temporary value) to be transferred to the object on the left hand side, possibly mutating the rvalue. With move semantics one can improve efficiency by overloading the copy constructor and assignment operator. Move Copy constructor: MyType(MyType&& other) Move Assignment Operator: MyType & operator=(MyType&& other)
How do you overload binary arithmetic operators?
Note how the + operator is defined in terms of += (which may be more efficient), and takes its lhs by COPY and returns its value by copy. class X { X& operator+=(const X& rhs) { // actual addition of rhs to *this return *this; } }; inline X operator+(X lhs, const X& rhs) { lhs += rhs; return lhs; }
How do you overload pointer dereference and member access functions?
Note that if value_type is not a primitive, the overloaded operator will be called recursively on the returned value. class my_ptr { value_type& operator*(); const value_type& operator*() const; value_type* operator->(); const value_type* operator->() const; };
How do you overload the function call operator?
Note that the function call operator should always be a member function! class foo { public: // Overloaded call operator int operator()(const std::string& y) { // ... } };
How do you overload the binary comparison operators?
Note that these should NOT be member functions! Also note that it is not advised to implement '||' or '&&' since shortcut semantics will be impossible. inline bool operator==(const X& lhs, const X& rhs){ /* do actual comparison */ } inline bool operator!=(const X& lhs, const X& rhs){return !operator==(lhs,rhs);} inline bool operator< (const X& lhs, const X& rhs){ /* do actual comparison */ } inline bool operator> (const X& lhs, const X& rhs){return operator< (rhs,lhs);} inline bool operator<=(const X& lhs, const X& rhs){return !operator> (lhs,rhs);} inline bool operator>=(const X& lhs, const X& rhs){return !operator< (lhs,rhs);}
How do you overload the stream insertion and deletion operators?
Note that these should not be member functions! std::ostream& operator<<(std::ostream& os, const T& obj) { // write obj to stream return os; } std::istream& operator>>(std::istream& is, T& obj) { // read obj from stream if( /* no valid object of T found in stream */ ) is.setstate(std::ios::failbit); return is; }
How do you overload the selection object?
Note the const variant for when this is a const variable: class X { value_type& operator[](index_type idx); const value_type& operator[](index_type idx) const; // ... };
How do overload increment/decrement?
Prefix: MyClass& MyClass::operator ++() { *this += 1; return *this; } Postfix: const MyClass MyClass::operator ++(int dummy) { MyClass oldValue = *this; // Store the current value of the object. ++*this; return oldValue; }
What is RAII? What problem does it address?
RAII stands for "Resource Acquisition is Initialization". It is an idiom for handling dynamically allocated resources which helps to prevent memory leaks. Each resource which needs to be manually allocated and freed is associated with the lifetime of another object. When the object goes out of scope, the destructor of the that object will free the resource.
What is const correctness?
Simply: code which uses const to explicitly say what is and is not mutated. Concretely: 1. Objects are never passed by value. Any object that would be passed by value is instead passed by reference-to-const or pointer-to-const. 2. Member functions which do not change state are marked const. Similarly, a function that is not marked const should mutate state somehow. Also all member functions marked const, are semantically const (not just bitwise const). 3. Variables which are set but never modified are marked const. Again, a variable not marked const should have its value changed at some point.
What is the assignment operator? When is it invoked? When is not invoked? What should it return?
The assignment operator is the function responsible for assigning a value to an object where both the objects in the assignment are of the same type. It would be invoked for instance in the following scenario: T a; T b; a = b; It won't be invoked if the left hand side of the assignment is a declaration. It is user-defined via operator overloading: MyType & operator=(const MyType & other) where the returned reference is a reference to this.
Under what scenarios can you overload the assignment operator to be between different classes?
The class being assigned to must be an ancestor of the class being assigned (the other way around doesn't work).
What is the copy and swap idiom?
The copy and swap idiom is an idiom for implementing the copy assignment operator. If a no-throw swap function is available for all the member objects and the class has a copy constructor and a destructor, one can write the copy assignment operator as follows: MyType & operator=(MyType other) { //swap all members of other with this //return a reference to this } What happens: 1) other is copy constructed from its argument 2) the members of other are swapped with this 3) other is destructed freeing all of the resources that this used to hold
What is shared_ptr? How are they constructed? Is there a slow and a fast way to construct them? How can you use them? How can you cast between different shared_ptr types?
shared_ptr is a smart pointer type which keeps track of the number of references to the object being managed. When the last smart_ptr which is managing an object is destroyed, the smart_ptr will then free the underlying memory. Constructor: shared_ptr<Thing> p1(new Thing); // two allocations shared_ptr<Thing> p(make_shared<Thing>()); // only one allocation! Uses: They can be used just like regular pointers (e.g. use '->', '==', '<', use as a condition in if or ternary statements, etc.)
What does std:swap do?
signature 1: template< class T > void swap( T& a, T& b ) noexcept(/* see below */); It is noexcept if the move copy constructor and move assignment operator are non-throwing. It swaps the values a and b. signature 2: template< class T2, std::size_t N > void swap( T2 (&a)[N], T2 (&b)[N]) noexcept(/* see below */); It is noexcept if swapp(T2& a, T2& b) is non-throwing.
Explain the various forms of casting and their uses.
static_cast: static_cast<Type>(object) Performs a compile time type conversion. There is no runtime check. Also note that if object's type is a virtual class base class of Type, or is a base class of a virtual base class of Type, the results are undefined. dynamic_cast: dynamic_cast<Type>(object) Used to downcast in a class hierarchy. If the cast is successful, dynamic_cast returns a value of type new_type. If the cast fails and new_type is a pointer type, it returns a null pointer of that type. If the cast fails and new_type is a reference type, it throws an exception that matches a handler of type std::bad_cast. don't use reinterpret_cast or C style casts (which might use a reinterpret_cast)
What is weak_ptr? What can you do with one? How do you construct one?
weak_ptr works in conjunction with a shared_ptr. It is also a wrapper around a raw pointer, but does not affect the reference count. It simply allows one to create a shared_ptr for the managed object and to determine if the managed object is still in existence. Ways to create a weak_ptr: shared_ptr<Thing> sp(new Thing); weak_ptr<Thing> wp1(sp); weak_ptr<Thing> wp2; // an empty weak_ptr - points to nothing wp2 = sp; Getting a shared_ptr from a weak_ptr: shared_ptr<Thing> sp2 = wp2.lock(); Checking if the managed object still exists: 1) get the shared_ptr using lock and check if it evaluates to 0 or false 2) construct a shared_ptr by passing the weak_ptr as an argument to the shared_ptr constructor (if it throws a std::bad_weak_ptr, then the managed object is gone)