Advanced Oop

By the end of this lesson you'll be able to share data across a class with static members, grant trusted access with friend , tame multiple inheritance and the diamond problem, query an object's real type at runtime with dynamic_cast , lock hierarchies down with final / override , and correctly manage resources with the rule of three/five.

Learn Python, JavaScript, Java and more with free interactive lessons, real projects and AI-powered help. Beginner-friendly.

Part of the free C++ course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.

Think of a class as a company . Each employee (object) has their own desk and tasks (instance members). But the company has one shared noticeboard everyone reads — that's a static member: it belongs to the company, not to any single person. A friend is the external auditor you deliberately give a key to the private filing cabinet. Multiple inheritance is an employee reporting to two managers — and the diamond problem is when both managers ultimately report to the same director: do you get one director or two? virtual inheritance guarantees there's only ever one director.

1. Static Members & Methods

A static member belongs to the class itself, not to any individual object — there's exactly one shared copy no matter how many objects you create. A static method can be called on the class ( BankAccount::howMany() ) without an object, but it can only touch static data because it has no this . One catch beginners always hit: a static data member must be defined once outside the class. Read this worked example, run it, then you'll write your own.

Your turn. The program below is almost complete — fill in the three blanks marked ___ using the 👉 hints, then run it.

2. friend Functions & Classes

Normally private members are off-limits to the outside world. A friend declaration is you deliberately granting one specific function — or an entire class — permission to reach inside. It's a controlled exception, not a leak: the class still decides exactly who gets in. The classic use is letting a printing helper or a tightly-paired class read your internals without exposing them to everyone.

3. Multiple Inheritance & the Diamond Problem

C++ lets a class inherit from more than one base at once. That power creates the diamond problem : when two bases ( Printer and Scanner ) both inherit the same ancestor ( Device ), a class inheriting both would get two copies of Device — so any member like serial becomes ambiguous. The fix is virtual inheritance : write class Printer : virtual public Device on both sides, and the compiler keeps a single shared Device . With a virtual base, the most-derived class is responsible for constructing it.

4. dynamic_cast , RTTI, final & override

RTTI (Run-Time Type Information) lets your program ask, while it's running, what an object's real type is. The tool is dynamic_cast : cast a base pointer down to a derived pointer and, if the object really is that type, you get a usable pointer — otherwise you get nullptr (no crash), which you test with an if . It only works on polymorphic types (those with at least one virtual function), because the check reads the vtable. Two safety keywords complete the picture: override makes the compiler verify you actually overrode a base method (catching signature typos), and final forbids any further overriding or subclassing.

Now you try. inspect receives a Shape* and should call radius() only when the shape is really a Circle . Fill in the two blanks:

5. The Rule of Three / Five

When a class owns a resource (heap memory, a file handle, a socket), the compiler's automatic copy behaviour is wrong — it copies the pointer , so two objects end up sharing and then double-freeing the same memory. The rule of three says: if you write any one of the destructor, copy constructor, or copy assignment, you almost certainly need all three. Modern C++ adds two more — the move constructor and move assignment — which cheaply steal a resource instead of copying it; together that's the rule of five . (The happiest path is the rule of zero : use std::string / std::vector / std::unique_ptr so you write none of them.)

No blanks this time — just a brief and an outline. Combine a static counter with a friend printer, build it, run it, and check your output against the example in the comments.

Practice quiz

A static data member of a class is best described as:

  • One copy per object
  • Always private
  • One copy shared by the whole class
  • A function with no body

Answer: One copy shared by the whole class. There is exactly one shared static member no matter how many objects exist; it must also be defined once outside the class.

Why can a static member function only access static members?

  • It has no 'this' pointer, so there is no object to reach instance members through
  • It runs at compile time
  • Static members are always public
  • It is implicitly const

Answer: It has no 'this' pointer, so there is no object to reach instance members through. A static method is called on the class itself (BankAccount::howMany()) with no object, so it has no 'this'.

What does a 'friend' declaration grant?

  • Inheritance from another class
  • Automatic copy construction
  • Thread safety
  • A specific function or class access to this class's private members

Answer: A specific function or class access to this class's private members. friend is a controlled exception that lets one named function or class reach the private members.

In the diamond problem, what does marking the inheritance 'virtual' achieve?

  • It makes methods virtual
  • It keeps a single shared copy of the common base instead of two
  • It deletes the base class
  • It speeds up dispatch

Answer: It keeps a single shared copy of the common base instead of two. virtual inheritance (class Printer : virtual public Device) keeps one shared Device, removing the ambiguity.

With a virtual base class, which class is responsible for constructing that base?

  • The most-derived class
  • The first base listed
  • Each intermediate class
  • The compiler, automatically with defaults only

Answer: The most-derived class. The most-derived class constructs the virtual base directly, e.g. MFP(sn) : Device(sn), Printer(sn), Scanner(sn).

dynamic_cast<Dog*>(animalPtr) returns what when the object is NOT a Dog?

  • A garbage pointer
  • It throws an exception
  • nullptr
  • It crashes

Answer: nullptr. On a pointer, a failed dynamic_cast yields nullptr, which you test with an if — no crash.

dynamic_cast only works on which kind of types?

  • Any type
  • Polymorphic types (with at least one virtual function)
  • Only POD structs
  • Only template types

Answer: Polymorphic types (with at least one virtual function). RTTI reads the vtable, so the type needs at least one virtual function (a virtual destructor is enough).

What does adding 'override' to a member function do?

  • Makes it virtual for the first time
  • Prevents inheritance
  • Makes it static
  • Tells the compiler to verify it actually overrides a base method, catching signature typos

Answer: Tells the compiler to verify it actually overrides a base method, catching signature typos. If your signature doesn't match a base virtual (e.g. a missing const), override turns a silent bug into a compile error.

The 'rule of three' says that if you write one of these, you likely need all three:

  • Constructor, getter, setter
  • Destructor, copy constructor, copy assignment
  • Move constructor, move assignment, swap
  • begin, end, size

Answer: Destructor, copy constructor, copy assignment. Managing a resource means defining the destructor, copy constructor, and copy assignment together (the rule of five adds the two move operations).

After Buffer c = std::move(a); in the rule-of-five example, what is the state of 'a'?

  • Unchanged, still holding its data
  • A deep copy of c
  • Moved-from: left empty but valid
  • Destroyed and unusable

Answer: Moved-from: left empty but valid. The move constructor steals a's pointer and sets a.data to nullptr, leaving 'a' empty but in a valid state.