C++ Notes
public, protected, or private.class Person { public: std::string name; int age; }; class Player : public Person { // public inheritance public: int score; };
private.main() or any unrelated class.| Parent Member | public inheritance | protected inheritance | private inheritance |
|---|---|---|---|
| public member | Stays public | Becomes protected | Becomes private |
| protected member | Stays protected | Stays protected | Becomes private |
| private member | Inaccessible in child | Inaccessible in child | Inaccessible in child |
Private members of the parent are never directly accessible in the child, regardless of inheritance type. They exist inside the object in memory, but the language blocks all direct access to them.
using keyword inside the desired access block.using also brings in all overloads of a method, not just one.class Child : private Person { protected: using Person::age; // resurrect age as protected using Person::getAge; // resurrect method (all overloads) };
When a child object is created, the parent part of the object must be built before the child part. This order is fixed by the compiler and cannot be changed.
Destructors are always called automatically in reverse order of construction. You never manually call a base destructor, the compiler handles it.
class Parent { protected: std::string name; int age; public: Parent(std::string name, int age) { this->name = name; this->age = age; } }; class Child : public Parent { double salary; public: Child(std::string name, int age, double salary) : Parent(name, age) // call base constructor in initializer list { this->salary = salary; } }; // in main: Child child("Hassan", 21, 50000);
Class obj2(obj1);, C++ automatically generates a copy constructor that performs a member-wise copy.| Java Behavior | C++ Behavior |
|---|---|
| Objects always live on the heap. Copying a reference copies the reference, not the object. Both variables end up pointing to the same object, a shallow copy by default. | Objects can live on the stack or the heap. The default copy copies actual data member by member. If the object has a stack array, the copy gets its own separate array, no shared memory. |
The default copy constructor works for heap objects too. Copying depends on the class definition, not on where the object lives. Copying a heap object: Class* obj2{ new Class(*obj1) };, note the dereference, you pass the object itself, not the pointer.
const reference to the same class as its parameter. Passing by value would cause infinite recursion, since copying the parameter would trigger the copy constructor again.other (a child object) to Parent(other), the compiler is smart enough to use only the parent portion of the object. This is called slicing.class Child : public Parent { int y; public: Child(int x, int y) : Parent(x), y(y) {} // custom copy constructor Child(const Child& other) : Parent(other), // slices and copies the base part y(other.y) // copies child-specific members { // deep copy logic for raw pointers goes here if needed } };
using Parent::Parent; inside the child class.class Parent { public: Parent(int x) { std::cout << "Parent constructor\n"; } }; class Child : public Parent { public: using Parent::Parent; // inherit all parent constructors }; // in main: Child obj(10); // calls Parent(int x) through inheritance
Use inherited constructors carefully. They only know about parent members, if the child has its own data members without default values, those will be left uninitialized.
delete on the child triggers the whole chain.virtual, and you delete a child object through a base pointer, only the base destructor runs. The child destructor is skipped entirely, causing a resource leak.virtual if you plan to use polymorphism or delete through a base pointer.Forgetting virtual on the base destructor is one of the most common bugs in C++ inheritance. Given Person* p = new Player(...); followed by delete p;, without virtual ~Person() the Player destructor never runs. Any heap memory the Player allocated in its own constructor leaks permanently, with no error to warn you.
virtual, this is called name hiding, not overriding. The child's version hides the parent's version entirely.main(), use child.Parent::display();, from inside the child class, use Parent::display();.virtual, then proper overriding happens instead, and polymorphism applies.#include <iostream> #include <string> using namespace std; // ============================================================ // BASE CLASS // ============================================================ class Person { private: string ssn; // private: NEVER directly accessible in child protected: int age; // protected: accessible in child class public: string name; // public: accessible everywhere Person(string name, int age, string ssn) : name(name), age(age), ssn(ssn) { cout << "Person constructor\n"; } Person(const Person& other) : name(other.name), age(other.age), ssn(other.ssn) { cout << "Person copy constructor\n"; } virtual ~Person() { cout << "Person destructor\n"; } int getAge() const { return age; } void display() const { cout << "Person: " << name << ", Age: " << age << endl; } }; // ============================================================ // PUBLIC INHERITANCE // ============================================================ class Player : public Person { private: int score; public: Player(string name, int age, string ssn, int score) : Person(name, age, ssn), score(score) // base constructor called first { cout << "Player constructor\n"; } // custom copy constructor: base part copied via slicing Player(const Player& other) : Person(other), score(other.score) { cout << "Player copy constructor\n"; } ~Player() { cout << "Player destructor\n"; } // name hiding (not virtual overriding) void display() const { cout << "Player: " << name << ", Age: " << getAge() << ", Score: " << score << endl; } }; // ============================================================ // PROTECTED INHERITANCE // ============================================================ class ProtectedChild : protected Person { public: ProtectedChild(string name, int age, string ssn) : Person(name, age, ssn) {} void show() { cout << "ProtectedChild name: " << name << endl; // name is now protected here } }; // ============================================================ // PRIVATE INHERITANCE + USING (resurrecting members) // ============================================================ class PrivateChild : private Person { protected: using Person::age; // resurrected as protected using Person::getAge; // resurrected method public: PrivateChild(string name, int age, string ssn) : Person(name, age, ssn) {} void show() { cout << "PrivateChild age: " << getAge() << endl; } }; // ============================================================ // INHERITING BASE CONSTRUCTORS VIA 'using' // ============================================================ class SimpleBase { public: SimpleBase(int x) { cout << "SimpleBase constructor with x = " << x << endl; } }; class DerivedUsingCtor : public SimpleBase { public: using SimpleBase::SimpleBase; // inherit all SimpleBase constructors }; // ============================================================ // MAIN // ============================================================ int main() { cout << "\n--- Object Construction Order ---\n"; Player p1("Hassan", 21, "123-ABC", 100); // prints: Person constructor, then Player constructor cout << "\n--- Copy Constructor ---\n"; Player p2 = p1; // calls Player copy constructor, which calls Person copy constructor cout << "\n--- Name Hiding ---\n"; p1.display(); // calls Player::display() (child version) p1.Person::display(); // explicitly calls Person::display() (parent version) cout << "\n--- Protected Inheritance ---\n"; ProtectedChild pc("Ali", 25, "456-DEF"); pc.show(); // pc.name; ERROR: name became protected, not accessible from main cout << "\n--- Private Inheritance + using ---\n"; PrivateChild pr("Sara", 30, "789-GHI"); pr.show(); // pr.name; ERROR: private in this context cout << "\n--- Inheriting Constructors ---\n"; DerivedUsingCtor obj(10); // calls SimpleBase(int) through inherited constructor cout << "\n--- Destruction Order (reversed, LIFO) ---\n"; // obj, pr, pc, p2, p1 destroyed in reverse order as main ends // for each: child destructor first, then parent destructor return 0; }
Access levels through inheritance, the fixed construction and destruction order, slicing in copy constructors, and the virtual destructor bug that catches almost everyone once. Last one left: polymorphism, where virtual stops being a footnote and becomes the whole point.