# Interfaces in C++ - The use of abstract classes with pure virtual functions ```cpp= class Shape { public: // Pure virtual function providing an interface framework. virtual int getArea() = 0; }; ``` - Derived classes must provide support for the pure virtual functions otherwise it'll raise a compilation error ```cpp= class Rectangle : public Shape { public: int getArea() override { return width * height; } }; class Triangle : public Shape { public: int getArea() override { return (width * height) / 2; } }; ``` - The abstract classes cannot be instantiated directly and serve as interfaces. The derived class that can be used to instantiate objects are called **concrete classes** like `Rectangle` and `Triangle` above - Interface inheritance is public inheritance and implementation inheritance is private inheritance. If class B publicly inherits from A, B is an A: it inherits the whole interface of A, and a (reference/pointer to) a B object can be automatically be upcasted to A, and used wherever an object of A is expected. However, if B privately inherits from A, B is-implemented-in-terms-of A: only the implementation of A is inherited, not its interface. Thus (references/pointers to) B objects can not be used in places where A objects are expected. # Type vs Interface vs Class in TS - Types define shape of data and can be used for type aliases - Interfaces define a contract that an object must adhere to and it can’t be used for type aliases - Use types when you need flexibility or want to create aliases - Use interfaces when defining contracts for objects or classes - If you want to create and pass a type-checked class object, use TypeScript classes. If you need to work without creating an object, an interface is more suitable ```typescript= // primitive type Name = string; // object type PartialPointX = { x: number; }; type PartialPointY = { y: number; }; // union type PartialPoint = PartialPointX | PartialPointY; // tuple type Data = [number, string]; ``` - Class and interface are considered static blueprints. Therefore, they can not implement / extend a type alias that names a union type ```typescript= interface Point { x: number; y: number; } class SomePoint implements Point { x = 1; y = 2; } type Point2 = { x: number; y: number; }; class SomePoint2 implements Point2 { x = 1; y = 2; } type PartialPoint = { x: number; } | { y: number; }; // FIXME: can not implement a union type class SomePartialPoint implements PartialPoint { x = 1; y = 2; } ``` - Unlike a type alias, an interface can be defined multiple times, and will be treated as a single interface (with members of all declarations being merged) ```typescript= // These two declarations become: // interface Point { x: number; y: number; } interface Point { x: number; } interface Point { y: number; } const point: Point = { x: 1, y: 2 }; ``` When to use type: - When defining an alias for primitive types (string, boolean, number, bigint, symbol, etc) - When defining tuple types - When defining function types - When defining a union - When trying to overload functions in object types via composition - When needing to take advantage of mapped types When to use interface: - Use interface for all object types where using type is not required (see above) - Use interface when you want to take advantage of declaration merging For more read [here](https://stackoverflow.com/a/65948871/22200202) # `std::make_unique()` in C++ - Utility function, defined in `<memory>` header file, used to create a `unique_ptr` object, which is a smart pointer that manages the lifetime of dynamically allocated objects ```cpp= std::make_unique <object_type> (arguments); ``` `arguments` is the argument list for the constructor of `object_type` # `static_cast` and `dynamic_cast` - Dynamic cast does runtime type checking, so pointer of superclass to subclass will not be type casted using `dynamic_cast<>` ```cpp= Parent par; Child ch; // class Child: Parent Parent *ptr = &ch; Parent *pt = &par Child *p = dynamic_cast<Child *>(ptr); Child *q = dynamic_cast<Child *>(pt); ``` `q` will be `nullptr` in this case and `p` is a valid pointer # The `const` paradigm - `const` is a compile time constraint - `const` means the variable can be put into ROM (useful in embedded programming) ### 1. `const` with functions - Only a member function of a class can be set as `const` - The function below can't change any of the member class variables' values but can only read them. It changes the type of `this` from `Foo* const` into `const Foo* const`. This is applicable only to non-mutable variables. In the expample below, `a` can still be changed but `b` cannot ```cpp= class Foo { mutable int a; int b; public: int Bar(int arg) const {} }; ``` - The compiler maintains a concept of bitwise constness - even a single bit can't be modified ```cpp= class BigArray { vector<int> v; // huge vector int accessCounter; int *vec; public: int getItem(int index) const { // accessCounter++; // Not allowed const_cast<BigArray *>(this)->accessCounter++; // This works since we casted away the constness of this pointer return v[index]; } int setItem(int index, int val) const { *(vec + index) = val; // This is allowed return vec[index]; } }; ``` Here, `setItem` maintains the bitwise constness since it doesn't change any member directly - For a normal function (outside class) `int Foo_Bar(const Foo* f, int arg)`, `Foo *f` cannot change the value of any member variable of `Foo` - A const object can only call const functions (the ones which don't change the values of any member variables) since a const object is not allowed to change the values of any member function / variable ```cpp= class C { int i; public: int Get() const { return i; } void Set(int j) { i = j; } }; void Foo(C &nonConstC, C const &constC) { int y = nonConstC.Get(); // Ok int x = constC.Get(); // Ok: Get() is const nonConstC.Set(10); // Ok: nonConstC is modifiable constC.Set(10); // Error! Set() is a non-const method and constC is a const-qualified object } ``` In the above code, the implicit `this` pointer to `Set()` has the type `C *const` whereas the pointer to `Get()` has type `C const *const`, indicating that the method cannot modify its object through the `this` pointer. - Similarly, a `const` function can only call other `const` functions. - In the example below, `constArray.Get()` calls the second `Get()` function which returns a non-modifiable value (`int const`) ```cpp= class MyArray { int data[100]; public: int & Get(int i) { return data[i]; } int const & Get(int i) const { return data[i]; } }; void foo( MyArray & array, MyArray const & constArray ) { // Get a reference to an array element and modify its referenced value. array.Get(5) = 42; // OK! (Calls: int & MyArray::Get(int)) // constArray.Get(5) = 42; // Error! (Calls: int const & MyArray::Get(int) const) } ``` - These two functions are basically the same, i.e., the compiler gives a compilation error stating that `setAge` cannot be overloaded ```cpp= void setAge(const int a) { age = a; } void setAge(int a) { age = a; } ``` - `const` reference return type ```cpp= class Dog { string name; public: Dog() { name = "doggo"; } const string &getName() { return name; } } ``` In this example, `getName` returns a reference to a const string, meaning that the characters of the return value cannot be changed. Using reference saves the space used ### 2. `const` with pointers - `int const * ptr` / `const int * ptr` - Variable pointer to constant integer ```cpp= int a = 10; const int b = 10; int const c = 10; int const * ptr = &c; // *ptr = 5; // Cannot do this ptr = &a; // *ptr = 5; // Canont do this a = 5; // This will change *ptr ``` - `int * const ptr` - Constant pointer to variable integer ```cpp= int a = 10; const int b = 10; int const c = 10; // int * const ptr = &b; // Cannot do this since b is const int type but we need int // int * const ptr = &c; // Cannot do this sicne c is const int typt but we need int int * const ptr = &a; *ptr = 5; // ptr = &b; // Cannot do this a = 10; ``` - `const * int ptr` - Incorrect declaration - `int const * const ptr` - Constant pointer to a constant integer ```cpp= int a = 1; const int b = 2; int const c = 3; int const * const ptr = &a; // *ptr = 5; // Cannot do this // ptr = &b; // Cannot do this a = 10; cout << *ptr << endl; ``` ### 3. `const` with variables - `int const x` is same as `const int x` - This will give errror because we cannot create a refernce to a constant integer ```cpp= void f(int& x); int const i; f(i); ``` ### 4. `constexpr` - Value of variable / function is constant and can be evaluated at compile time. - However, `constexpr` functions can be evaluated at compile time, but also during run time if compile time evaluation is not possible ```cpp= class A { int x = 3; public: void set(int val) { x = val; } constexpr int func() { return x; } }; A a; int x = a.func(); // This is allowed since x is int cout << x << endl; a.set(10); cout << x << endl; // constexpr int y = a.func(); // This gives compilation error since y is constexpr and a.func() doesn't return compile-time evaluatable value. ``` However, this is still valid ```cpp= const int x = 10; constexpr int y = x; ``` ### 5. `const_cast` - Used to cast away the const-ness of a `const` variable. ```cpp= const int a = 1; cout << a << endl; // Prints 1 obviously const_cast<int &>(a) = 10; // a = 5 // Not allowed cout << const_cast<int &>(a) << endl; // Prints 10 obviously cout << a << endl; // Prints 1 WTF? ``` - The type or const-ness of a variable is not changed directly ```cpp= int x = 10; const_cast<const int &>(x); // Cast x to a const x = 5; // This is still allowed cout << x << endl; // Prints 5 cout << const_cast<const int &>(x) << endl; // Prints 5 // const_cast<const int &>(x) = 5; // This is not allowed since it represents a const ``` # Compiler Generated Functions - A default constructor is a constructor that can work without any parameter. `A(int x = 0)` is also a default constructor for `class A` - All compiler generated functions are public and inline. - They are generated only if they are needed. They will not be generated if they are not used - A compiler generates the default constructor only when there is no user defined constructor (default or parametrized) ```cpp= // class Dog {}; is equivalent to class Dog { public: // Copy constructor Dog(const Dog &dog) { ... }; // Member-by-member intialization // Copy assignment constructor Dog &operator=(const Dog &dog) { ... }; // Member-by-member copying /* * 1. Call base class's default constructor * 2. Call data member's default construcor */ Dog() { ... }; /* * 1. Call base class's destructor * 2. Call data member's destructor */ ~Dog() { ... }; } ``` - `const` and reference cannot be copied, they can only be initialized - The following code gives compilation error > error: no matching function for call to Dog::Dog() > the default constructor of "Dog" cannot be referenced -- it is a deleted function ```cpp= class Collar { public: Collar(string color) { cout << "Collar is made\n"; } }; class Dog { Collar m_collar; }; int main() { Dog dog; } ``` Since the `class Collar` has no default constructor, the compiler won't be able to make the default constructor for `class Dog`. The compiler will first try to make a default constructor for `class Collar` but since it already has a parametrized constructor, it wont't be able to make one. - This is used to tell compiler to use compiler generated constructor as default constructor ```cpp= class Dog { public: Dog() = default; }; ``` ### Inaccessible functions - We can prevent user from creating a class object without the correct parameters ```cpp= class OpenFile { public: OpenFile(string filename) { ... } }; int main() { // OpenFile f(); // This will give a compilation error OpenFile f(string("/path/to/file")); } ``` The compiler doesn't generate the default constructor, so the first call gives an error. The user can create an object only by specifying the name of the file. - We can delete / remove member functions to prevent compiler from generating those functions ```cpp= class OpenFile { public: OpenFile(string filename) { ... } OpenFile(const OpenFile &other) = delete; }; int main() { OpenFile f(string("/path/to/file")); OpenFile file(f); } ``` Alternatively, we can also declare the copy constructor as private. ```cpp= class OpenFile { private: OpenFile(const OpenFile &other); public: OpenFile(string filename) { cout << filename << endl; } OpenFile createFile(OpenFile &f) { return OpenFile(f); } }; int main() { OpenFile f(string("/path/to/file")); // OpenFile file = f.createFile(f); // This will fail too since the function body is not defined } ``` Now, nobody can use the copy constructor, not even other methods of the class. This way of making methods private can be used to make any method (even inherited) unusable, as long as you don't write the function body. - Making the desructor private, will lead to a compilation error. Only another method / friend can destroy such a class object. This is used in reference counting shared pointers (counts the number of pointers that point to itself, once count = 0, it kills itself). ```cpp= class OpenFile { private: ~OpenFile() { cout << "File deleted\n"; } public: OpenFile(string filename) { cout << filename << endl; } void destroy() { delete this; // This invokes the private destructor. } }; int main() { OpenFile f(string("/path/to/file")); f.destroy(); OpenFile *file = new OpenFile(string("path.txt")); file->destroy(); } ``` This will still give the same error, since after `f.destroy()` is called, when the stack unwinds, the destructor will still be called (which doesn't exist). However, `file` will be deleted properly since it is not allocated on the stack, so the destructor is not called for this. A class with private destructor can only be stored on heap and not on stack. ### Destructors - The following code outputs only ***"Dog Destroyed"*** ```cpp= class Dog { public: ~Dog() { cout << "Dog Destroyed\n"; } }; class GreenDog: public Dog { public: ~GreenDog() { cout << "Green Dog Destroyed\n"; } }; class DogFactory { public: static Dog *createGreenDog() { return new GreenDog(); } }; int main() { Dog *dog = DogFactory::createGreenDog(); delete dog; } ``` However, if we make `~Dog()` `virtual`, it ensures that the destructors are called in proper order of inheritance. Alternatively, you can use a `shared_ptr` ```cpp= class Dog { public: ~Dog() { cout << "Dog Destroyed\n"; } }; class GreenDog: public Dog { public: ~GreenDog() { cout << "Green Dog Destroyed\n"; } }; class DogFactory { public: static shared_ptr<Dog> createGreenDog() { return shared_ptr<GreenDog>(new GreenDog()); } }; int main() { shared_ptr<Dog> dog = DogFactory::createGreenDog(); } ``` We don't need to destroy the dog pointer, the `shared_ptr` does it on its own. **NOTE:** STL classes have no virtual destructors, so we need to be careful while inheriting from them. # Types of Pointers ### Smart Pointers - If a class has a constructor that can be called with only one argument, then this becomes a conversion constructor. It allows the conversion of the single argument to the class being constructed. ```cpp= class Complex { double real, im; public: Complex(double r = 0.0, double i = 0.0) { real = r; im = i; } bool operator==(Complex &other) { return (real == other.real && im == other.im); } }; int main() { Complex num(3.0, 0.0); cout << (num == 3.0); } ``` This will print `1` since `3.0` gets converted to an object of `class Complex`. To prevent this, we can use `explicit` constructor, i.e., `explicit Complex(double r = 0.0, double i = 0.0)` # Constructors and Destructors - As control passes from a throw-expression to a handler, destructors are invoked for all automatic objects constructed since the try block was entered. The automatic objects are destroyed in the reverse order of the completion of their construction. - Constructors cannot be virutal - compilation error - Child class destructor always calls parent class destructors (if multiple parents exist, then in the reverse order of inheritance) - Child class constructor first calls constructors of parent classes (in order of inheritance in the case of multiple inheritance) then calls its own constructor. ### Delegating Constructors - Reduce repetitive code in constructors and delegate some of the work to another constructor. Eg: ```cpp= class class_c { public: int max; int min; int middle; // class_c() {} // class_c(int my_max) { // max = my_max > 0 ? my_max : 10; // } // class_c(int my_max, int my_min) { // max = my_max > 0 ? my_max : 10; // min = my_min > 0 && my_min < max ? my_min : 1; // } // class_c(int my_max, int my_min, int my_middle) { // max = my_max > 0 ? my_max : 10; // min = my_min > 0 && my_min < max ? my_min : 1; // middle = my_middle < max && my_middle > min ? my_middle : 5; // /* Instead of doing this, do the following */ class_c(int my_max) { max = my_max > 0 ? my_max : 10; } class_c(int my_max, int my_min) : class_c(my_max) { min = my_min > 0 && my_min < max ? my_min : 1; } class_c(int my_max, int my_min, int my_middle) : class_c (my_max, my_min) { middle = my_middle < max && my_middle > min ? my_middle : 5; } }; ``` ### Copy and Move Constructors - If any constructor is being called, it means a new object is being created in memory. So, the only difference between a copy constructor and a move constructor is whether the source object that is passed to the constructor will have its member fields copied or moved into the new object. - Imagine an object containing a member pointer to some data that is elsewhere in memory. For example, a `std::string` pointing at dynamically allocated character data. Or a `std::vector` pointing at a dynamically allocated array. Or a `std::unique_ptr` pointing at another object. - A copy constructor must leave the source object intact, so it must allocate its own copy of the object's data for itself. Both objects now refer to different copies of the same data in different areas of memory. - A move constructor, on the other hand, can simply "move" the data by taking ownership of the pointer that refers to the data, leaving the data itself where it resides. The new object now points at the original data, and the source object is modified to no longer point at the data. The data itself is left untouched. ### Virtual Functions and Destructors - If virtual present $\rightarrow$ Dynamic binding (based on the object it points to) - If virtual absent $\rightarrow$ Static binding (based on the pointer type) ```cpp= class A { public: A() { cout << "A constructor\n"; } void foo() { cout << "A\n"; } ~A() { cout << "A destructor\n"; } }; class B: public A { B() { cout << "B constructor\n"; } void foo() { cout << "B\n"; } ~B() { cout << "B destructor\n"; } }; class C: public B { C() { cout << "C constructor\n"; } void foo() { cout << "C\n"; } ~C() { cout << "C destructor\n"; } }; int main() { A *ptr = (A*)new C(); // constructors: A -> B -> C ptr->foo(); // A delete ptr; // destructors: A } ``` - If the destructor of the pointer type (`A` in this case) was virtual, then the destructor of the actual object (`C` in this case) would've been called, otherwise the destructor of pointer type (`A`) is called by default; # Common Optimizations - Normally if you create a vector in a function and try to return the local vector as such: ```cpp= vector<int> foo() { vector<int> v; // fill v return v; // NRVO example } vector<int> vec = foo(); ``` one would expect that the vector is copied twice, once while returning from foo and another time while allocating `vec`. However, due to copy elision, in this case specifically Named Return Value Optimization (NRVO), move semantics act and the vector is moved to `vec` rather than being copied. **Instead of creating a local return object and then moving/copying it in place of the function call, NRVO instantly creates it in the called place.** - Return Value Optimization (RVO) - **Instead, the returned object is constructed in place of the function call. It does not allow the creation of a local object that is used as a return value.** ```cpp= class A { public: A() { cout << "GeeksforGeeks"; } A(const A&) { cout << " GeeksforGeeks Copy Constructor"; } }; A func() { return A(); // RVO example } int main() { A a = func(); return 0; } ``` The output of this code is just `GeeksforGeeks`, since the copy constructor is not called at all.