Rule of Zero/Three/Five

Constructor

Same named function of class. Ctr is cal led when object of class is created. Used to provide values to data members.
Return type=None, Cannot access to address of ctr, Inheriting ctr=Cannot
PRIVATE SCOPED CTR*: Can ctr be inside private section(Yes. Single DP)

Ctr calling Hierarchy (Base Class > Derv Class Ctr)

Base class constructor should be defined

Ctr Initializer list

Derived class object also have inherited properties of Base class,
and only base class constructor can properly initialize base class members

class A{
  public:
    A() { cout << "base ctr";  }
    ~A() { cout << "base dtr";  }
};
    
class B: public A {
  public:
    B() { cout << "Derv ctr";  }
    //What the compiler turns B() into
    //B() : A() {..} Implicitly calls A's default constructor 1st

    ~B() { cout << "Derv dtr";  }
    // What the compiler turns ~B() into:
    //~B() {
      //  cout << "~B";  // Your custom destructor code runs first
      // Compiler automatically inserts the base destructor call here at the end:
      //A::~A();
    //}
};
int main(){
    B obj;
}
$ a.out
base ctr
Derv ctr
Derv dtr
Base dtr
            
C++ will always call Base ctr 1st
Either user has provide definition (A(){})
Or user can remove his contructor. Compiler will add its default ctr

class A{
public:
    A();
    ~A();
};
class B: public A {
public:
    B() { cout << "B ctr";  }
    ~B() { cout << "~B";  }
};
int main(){
    B obj;
}
# ./a.out
Error cannot find A::A()
                
In the initializer list, the order of execution takes place according to the order of declaration of member variables.

class test{
    int a; //Declared 1st
    int b; //Declared 2nd
public:
    test (int x): b(x), a(b * 2) {
        cout << b << a;       //b=10, a=garbage
    }
};
int main() {
    test obj(10);
    return 0;
}
$ ./a.exe
b = 10
a = garbage value
This is because, a is declared before b. Initialization of a will happen before b.
Since a = b*2, and b does not have value hence a is initialized to garbage.
        

Types of Constructors

1. Copy Constructor

Copy Ctr is L Value Refererence. Creates copy of object
Default Copy Constructor(Shallow Copy) User-Defined Copy Constructor (Deep Copy)
If you don't define a copy constructor, the compiler provides a default one that performs a shallow, member-wise copy.
This works well for simple classes without dynamically allocated memory.
#include <iostream>
using namespace std;

class A {
	int a;
public:
    // Default copy ctr. Does not have &
	A(int x):a(x) {}
	void print() { cout << a << "\n"; }
};
int main() {
	A obj1(1);
	A obj2(obj1);
	A obj3 = obj1;
	obj3.print();       //1
	obj2.print();       //1
	return 0;
}
A user-defined copy constructor is necessary when a class has a pointers to dynamically allocated memory.
The default shallow copy would cause both objects to point to the same memory location, (e.g., a double-free error) when freed
user-defined constructor below performs a deep copy by allocating new memory for the copied object.
#include <iostream>
#include <cstring>
using namespace std;

class A {
	char *a;
public:
	A(const char* x) {
		a = new char[strlen(x)+1];
		strcpy(a, x);
	}
    // Deep copy ctr
    // The argument to the copy constructor is const reference:
    //  This prevents accidental modification of the source object
    //  and to avoid infinite recursive calls during object creation
	A(const A& obj) {
		a = new char[strlen(obj.a) + 1];
		strcpy(a, obj.a);
	}
	~A() {
		delete[] a;
	}
	void print() {
		cout << a << endl;
	}
};
int main() {
	A obj1("Hello");
	A obj2 = obj1;
	obj2.print();           //Hello
	return 0;
}

2. Move Constructor(rvalue reference) (Introduced in c++11)

Move Semantics?

Helps transfer of ownership of resources from one object to another without performing an deep copy.
Move constructor implements move semantics

Move Constructor

Moves the object.
Useful for temporary objects (rvalues) which are about to be destroyed anyway, instead of creating new deep copied object, Just transfer the ownership

using vi = vector <int>;
class A {
    vi a;
    void print() {
        for (decltype(auto)i:a)
            cout << i;
    }
public:
    //A(vi&& b):a(b)       ///////// This will make copy. Not correct move ctr ///////////
    A(vi&& b):a(move(b)) {  // move ctr (using rvalue ref).
        cout << "\nmove ctr\n";
        print();
    }
};
int main() {
    vi a = {1,2,3};
    A obj(a);           // Copy ctr called. Local copy remains present

    // move invokes a r value ref
    A obj1(move(a));            // move(a) is equal to (int &&x = a)
    cout << "\nAfter move ctr in main()\n";
    for(decltype(auto)i:a)
        cout << i;
}
/*
$ ./a.out
move ctr
1 2 3

After move ctr in main()    //Value moved not availble in main
$
*/
        

delete the contructor

Introduced in C++11, = delete is an instruction telling the compiler to explicitly disable a function. If any code tries to call that function—or if the compiler will immediately throw a compile-time error
Before C++11 Programmers used to hide functions by making them private and leaving them unimplemented to prevent copying. However, that old trick only caused linker errors later in the build process when friend functions call it

class Database {
public:
    Database() = delete; // Cannot do: Database db;
    Database(string connectionString); // Must use this
};

Use Cases of =delete constructor

1. Classes that require parameters: Ensuring a class must be initialized with specific data (e.g., A(int x) is provided, but A() = delete; forces users to pass an integer).
2. Static-only utility classes: Classes that act merely as a namespace for static helper functions and shouldn't ever be instantiated as objects.
3. Singletons: Preventing multiple instances from being created accidentally via default construction.