Advanced C++ Flashcards
Learn all the nitty-gritty details of C++ specifically
What is the difference between a class and a struct?
A C++ struct is simply a class where the default visibility is public. This also applies to inheritance - inheriting from a struct will always use public inheritance by default.
What does the static binding do?
Static is a binding that associated with the program’s lifetime, rather than the immediate scope. Assignment can be global (instantiates at program start), or local (instantiates when execution reaches that point). In either case, they are destroyed only at program end.
Consider the following code. What is the output?
int main() {
for (int i = 0; ++i; i < 4)
printNewNum();
}
void printNewNum() {
static int x = 0;
printf(“%d\n”, x++);
}
0
1
2
3
What is a constructor?
A constructor is a method called when an object is constructed. They are used to initialise the object’s variables, and have no return type. If one isn’t provided, it will be automatically generated.
For a class ‘MyClass’, write down the signatures of the available constructor types.
- Constructor - MyClass();
- Destructor - ~MyClass();
- Copy constructor - MyClass(const MyClass &other);
- Copy assignment constructor - MyClass& operator=(const MyClass &other);
- Move constructor - MyClass(const MyClass &&other);
- Move assignment constructor - MyClass& operator=(const MyClass &&other);
What is a destructor?
A destructor is a method that is called on object destruction, and is intended for cleaning up dynamically allocated memory or handles. As with constructors, one will be automatically provided if not given (though this will be non-virtual by default). They also lack a return type.
Consider the following code. What problem could occur when this is run, and how can it be fixed?
class A {
public:
A() { std::cout «_space;“A Constructor” «_space;std::endl; }
~A() { std::cout «_space;“A Destructor” «_space;std::endl; }
}
class B : public A {
public:
B() { std::cout «_space;“B Constructor” «_space;std::endl; }
~B() { std::cout «_space;“B Destructor” «_space;std::endl; }
}
void main() {
A *a = new B();
delete a;
}
When object ‘a’ is destroyed, it will call A’s destructor, but not B’s destructor as well. This can lead to memory leaks or improper cleanup.
We can fix this by making A’s destructor virtual. This will ensure that B’s destructor gets called before A’s.
Consider this class. What order are the following destroyed in?
class A, class B, class C, string printStr, int wheels, string name
class D : public A, virtual B, C {
public:
~D() {
string printStr = “Goodbye!”
printf(“%s”, printStr);
}
int wheels;
string name;
}
The destruction order of items in a class is as follows:
01) Class destructor body.
02) Destructors for non-static member objects are called in reverse order of declaration.
03) Destructors for inherited members are called in reverse order of declaration. Non-virtual base class destructors are always called first before any virtual ones however.
So the destruction order for the for an object D is string printStr, string name, int wheels, class A, class C, class B.
List several circumstances in which a destructor may be called.
Explicit destructor call.
Local object goes out of scope.
Dynamically allocated object is deallocated with delete.
Lifetime of a temporary object ends (e.g. rvalues).
When program ends (for global and static variables).
What is the diamond problem regarding inheritance? How can it be resolved?
The diamond problem occurs when a class inherits from two classes who themselves inherit from the same class (forming a diamond-shaped inheritance structure). What actually happens is that the intermediate classes create separate instances of the base class to inherit methods from. This leads to ambiguity over which instance to call the base method from.
This can be resolved using virtual inheritance. This forces the intermediate classes to inherit from the same instance of the base class, so only one version of the inherited method exists.
What is the most vexing parse? What is one way to overcome it?
Most vexing parse refers to ambiguity when the compiler tries to determine whether a phrase is object construction with default constructor or a function prototype with no parameters that returns class type. In the C++ standard, it is required it is interpreted in the latter way as function prototypes are prioritised over construction.
One way to overcome this is to use brace initialization, that was introduced in C++11. This unambiguously refers to constructing objects, rather than function prototypes.
Describe the access modifiers available in C++.
Public - Accessible by any other module.
Protected - Accessible only by inheriting classes.
Private - Accessible only within class.
Describe the visibility of x/y/z to an inheriting class B if A is inherited with public, protected and private visibility respectively.
class A {
public int x;
protected int y;
private int z;
}
x is public, protected, private
y is protected, protected, private
z is inaccessible, inaccessible, inaccessible
List some of the uses of the const keyword.
Const variables declare that the value of that variable cannot be changed after initialisation.
If a method is declared const, it cannot change the object’s state when called (unless member variable is mutable). Const class instances can only call const methods. Const methods may also only call other const methods.
What is the difference between the following?
- const int* a
- int* const a
const int* a - a is a variable pointer to a constant integer
int* const a - a is a constant pointer to a variable integer