C++ Interview Note [ c++ interview ] Dec 19, 2022 Intro This is a note for my c++ SWE interview. Notion page Due to personal reasons, I’ve decided to write new notes in this Notion page (chinese). Below sections are my old notes. How to stop people deriving from my class? source: Bjarne Stroustrup’s FAQ First, why do you want to? There are two common answers: for efficiency: to avoid my function calls being virtual for safety: to ensure that my class is not used as a base class (for example, to be sure that I can copy objects without fear of slicing) According to Stroustrup, the efficiency reason is usually misplaced fear. In C++, virtual function calls are so fast that their real-world use for a class designed with virtual functions does not to produce measurable run-time overheads compared to alternative solutions using ordinary function calls. Note that the virtual function call mechanism is typically used only when calling through a pointer or a reference. When calling a function directly for a named object, the virtual function class overhead is easily optimized away. Ok, now how to stop people deriving from my class? In C++11, the solution is the final specifier. struct Base { virtual void f(); }; struct Derived final : Base { // now Derived is final; you cannot derive from it void f() override; }; struct DD : Derived { // error: a 'final' class type cannot be used as a base class // ... }; Sounds easy. However, in my interview, the interviewer didn’t allow me to use final. Thanks to Stroustrup, I now know the answer. For older compilers, you can use a clumsy technique: class Usable; class Usable_lock { friend class Usable; private: Usable_lock() {} Usable_lock(const Usable_lock&) {} }; class Usable : public virtual Usable_lock { // ... public: Usable(); Usable(char*); // ... }; Usable a; class DD : public Usable {}; // ok, but... DD dd; // error: DD::DD() cannot access // Usable_lock::Usable_lock(): private member Virtual in C++ Virtual Function / Runtime polymorphism source: Geeksforgeeks: Virtual Function in C++ A virtual function is a member function which is declared within a base class and is re-defined (overridden) by a derived class. If we have created a virtual function in the base class and it is being overridden in the derived class, then we don’t need virtual keyword in the derived class. e.g. derived::print() is automatically considered as a virtual function. When you refer to a derived class object using a pointer or a reference to the base class, you can call a virtual function for that object and execute the derived class’s version of the function. This is called runtime polymorphism. Basic example: // code adapted from https://www.geeksforgeeks.org/cpp-polymorphism/ #include <bits/stdc++.h> using namespace std; class base { public: virtual void print() { cout << "print base class" << endl; } void show() { cout << "show base class" << endl; } }; class derived : public base { public: // print () is already virtual function in // derived class, we could also declared as // virtual void print () explicitly void print() { cout << "print derived class" << endl; } void show() { cout << "show derived class" << endl; } }; int main() { base *bptr; derived d; bptr = &d; // Virtual function, binded at // runtime (Runtime polymorphism) // output: print derived class bptr->print(); // Non-virtual function, binded // at compile time // output: show base class bptr->show(); return 0; } Virtual Inheritance / Virtual Base Class source: wiki: Virtual inheritance, Geeksforgeeks: Virtual base class Virtual inheritance is a C++ technique that ensures only one copy of a base class’s member variables are inherited by grandchild derived classes. Consider the situation where we have one class A .This class A is inherited by two other classes B and C. Both B and C are inherited by class D. This is called diamond problem. A / \ B C \ / D Now let’s try to implement this graph without using virtual. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 #include <iostream> using namespace std; class A { private: string _msg; public: A(std::string x) : _msg(x) { cout << "A constructor\n"; } void show() { cout << "show " << _msg << "\n"; } }; class B : public A { public: B(std::string x) : A("b") { cout << "B constructor\n"; } }; class C : public A { public: C(std::string x) : A("c") { cout << "C constructor\n"; } }; class D : public B, public C { public: D(std::string x) : B("d_b"), C("d_c") { cout << "D constructor\n"; } }; int main() { D d_object("useless"); // output: A constructor // B constructor // A constructor // C constructor // D constructor // d_object.show(); // compile error: "D::show" is ambiguous // A& a = d_object; // compile error: base class "A" is ambiguous // solution 1: use compile time cast A& Aref_cast_from_D_to_B = static_cast<B&>(d_object); Aref_cast_from_D_to_B.show(); // output: show b A& Aref_cast_from_D_to_C = static_cast<C&>(d_object); Aref_cast_from_D_to_C.show(); // output: show c // solution 2: use scope resolution d_object.B::show(); // access B's A, output: show b d_object.C::show(); // access C's A, output: show c } We got error in line 45 and 47. The reason is that, a D will contain two copies of A’s member variables: one via B, and one via C. These two copies could be accessible by using static cast or scope resolution). To sum up, when any data or function member of class A is accessed by an object of class D, ambiguity arises. Now, let’s see how we use virtual specifier to avoid ambiguity. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 #include <iostream> using namespace std; class A { private: string _msg; public: A(std::string x) : _msg(x) { cout << "A constructor\n"; } void show() { cout << "show " << _msg << "\n"; } }; class B : virtual public A { // virtually inherits A. B becomes virtual base class. public: B(std::string x) : A("b") { cout << "B constructor\n"; } }; class C : virtual public A { // virtually inherits A. C becomes virtual base class. public: C(std::string x) : A("c") { cout << "C constructor\n"; } }; class D : public B, public C { public: D(std::string x) : A("d_a"), B("d_b"), C("d_c") { // need to call A's constructor cout << "D constructor\n"; } }; int main() { D d_object("useless"); // output: A constructor // B constructor // C constructor // D constructor d_object.show(); // output: show d_a A& a = d_object; // good // use compile time cast A& Aref_cast_from_D_to_B = static_cast<B&>(d_object); Aref_cast_from_D_to_B.show(); // output: show d_a A& Aref_cast_from_D_to_C = static_cast<C&>(d_object); Aref_cast_from_D_to_C.show(); // output: show d_a // use scope resolution d_object.B::show(); // output: show d_a d_object.C::show(); // output: show d_a } Now, a D has only one, shared A instance in its representation, so there’s no ambiguity anymore. Virtual Destructor source: geeksforgeeks: Virtual Destructor Deleting a derived class object using a pointer of base class type that has a non-virtual destructor results in undefined behavior. To solve this problem, the base class should be defined with a virtual destructor. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 #include <iostream> using namespace std; class base { public: base() { cout << "Constructing base\n"; } virtual ~base() { cout << "Destructing base\n"; } // virtual! }; class derived : public base { public: derived() { cout << "Constructing derived\n"; } virtual ~derived() { cout << "Destructing derived\n"; } // the virtual specifier here isn't necessary }; int main() { derived *d = new derived(); // output: Constructing base // Constructing derived base *b = d; delete b; // output: Destructing derived // Destructing base return 0; } According to Scott Meyers’ Effective C++: Always declare destructors virtual in polymorphic base classes. If a class has any virtual function, it should have a virtual destructor. Classes not designed to be base classes or not designed to be used polymorphically should not declare virtual destructors. Please enable JavaScript to view the comments powered by Disqus.