ByteWise

Section 6: Keypoints of C++

Why This Section Exists

Every section so far paired Python code with C++ code, and every C++ block quietly introduced a piece of syntax that Python simply doesn't have — a semicolon rule, an access specifier, a virtual keyword. None of those were wrong to skim past in the moment, since the concept (encapsulation, abstraction, inheritance, polymorphism) was always the point, not the syntax.

But before moving on to SOLID and design patterns, it's worth stopping and collecting every one of those C++-specific pieces in one place — a single page to come back to whenever a C++ line looks unfamiliar.

💡🗣️ Conversational

Think of this section as the "glossary of C++ keywords" for everything you've already learned. Nothing new conceptually happens here — it's just gathering the syntax you've already seen, scattered across five sections, into one page.

The Full Quick-Reference Table

C++ FeatureSyntaxFirst Seen In
Class body must end with a semicolon};Section 1
Access specifiers (compiler-enforced)private: / protected: / public:Section 2, Section 4
Constructor with an initializer listAccount(double b = 0) : balance(b) {}Section 2
const-correctness on methodsdouble getBalance() constSection 3
Pure virtual function (interface contract)virtual void execute() = 0;Section 3
override keywordvoid execute() overrideSection 3, Section 4
Declaring inheritanceclass MarketOrder : public OrderSection 4
Calling the parent's constructorOrder(t, q) inside the child's initializer listSection 4
virtual keyword (enables runtime dispatch)virtual void execute()Section 5
Virtual destructorvirtual ~Order() {}Section 3 (used, not yet explained)
Storing mixed subclasses safelystd::vector<std::unique_ptr<Order>>Section 5

The rest of this page walks through the entries that deserve more than one table row.

1. Access Specifiers — Compiler-Enforced, Not Just a Convention

class Account {
private:
    double balance;   // only Account's own methods can touch this

protected:
    // accessible inside Account AND any class that inherits from it

public:
    // accessible from anywhere
};
💡🗣️ Recap

Python's self.__balance is a "please don't touch" sign — you technically still can. C++'s private: is a locked door with no handle on the outside; the compiler physically refuses to compile code that breaks it. protected: sits in between: open to the class and its subclasses, closed to everyone else.

2. Constructors and the Initializer List

Account(double b = 0) : balance(b) {}
//       ^parameter    ^initializer list   ^empty body
  • Everything after the : and before { is the initializer list — it sets each field's starting value directly, before the constructor's body even runs.
  • LimitOrder(std::string t, int q, double lp) : Order(t, q), limitPrice(lp) {} does two things at once: calls the parent's constructor (Order(t, q)) and initializes its own new field (limitPrice(lp)).
  • Python has no equivalent syntax — super().__init__(...) and self.x = ... both happen as plain statements inside __init__'s body.

3. const-Correctness

double getPrice() const {
    return price;
}

Technical: the trailing const is a promise to the compiler — "calling this method will not modify any field of the object." If the method body tries to assign to price (or call any non-const method), the compiler rejects it.

💡🗣️ Conversational

Think of const on a method as a label that says "read-only, guaranteed." Any getter — getBalance(), getPrice() — should almost always be const, because a getter's entire job is to look, not touch. Python has no equivalent; there's no way to mark a Python method as "promises not to mutate self."

4. The Big Three Dispatch Keywords: virtual, override, = 0

These three keywords work together but mean three different things — easy to blur, worth separating cleanly:

KeywordGoes OnMeaning
virtualThe base class method"Subclasses are allowed to override this — resolve the call at runtime based on the object's real type, not the pointer's declared type."
overrideA subclass method"I intend to override a virtual method from my parent." The compiler checks this claim is true — a typo'd method name fails to compile instead of silently creating a new, unrelated method.
= 0A base class method, added after virtual"This method has no implementation at all — every concrete subclass must provide one, or that subclass can't be instantiated either." This is what makes a class an interface.
⚠️🧠 The Trap `virtual` Prevents

If you forget virtual on Order::execute(), calling execute() through an Order* pointer will always run Order's version — even if the object underneath is really a MarketOrder. No error, no warning — just silently wrong behavior. Python never has this trap because it always looks up the real object's type, every time, automatically.

5. Virtual Destructors — Why virtual ~Order() {} Was There All Along

You've seen virtual ~Order() {} sitting quietly in the base classes since Section 3, without an explanation. Here's the reason it matters:

The problem it solves: if you delete an object through a base-class pointer (e.g., Order* o = new MarketOrder(...); delete o;), and the destructor isn't virtual, C++ only runs Order's destructor — not MarketOrder's. Any cleanup MarketOrder needed (releasing memory, closing a file) never happens. That's a real, silent resource leak.

The fix: marking the base class destructor virtual makes delete look up the object's real type first — same runtime-dispatch idea as virtual on any other method, just applied to destruction.

ℹ️Rule of Thumb

Any class meant to be used as a base class for polymorphism (i.e., any class with at least one other virtual method) should give its destructor virtual too — even if the destructor's body is empty. It costs nothing and prevents a subtle, hard-to-spot leak.

6. Object Slicing and Why Section 5 Needed std::unique_ptr

Recap from Section 5: writing std::vector<Order> orders; and pushing MarketOrder/LimitOrder objects into it slices each one down to a plain Order-sized box, discarding everything that made it a MarketOrder. Polymorphism then silently breaks — every execute() call runs Order's generic version.

std::vector<std::unique_ptr<Order>> orders;   // pointers — no slicing, real type preserved
orders.push_back(std::make_unique<MarketOrder>("AAPL", 10));
💡🗣️ Conversational

Python never runs into slicing because Python variables are always references under the hood — there's no equivalent of "copying an object down to a smaller box." C++ requires pointers (unique_ptr being the modern, safe choice) specifically to sidestep this.

Side-by-Side: Every Keyword, One More Time

Python Has No Equivalent For...Because...
private: / protected: (compiler-enforced)Python relies on naming convention (_x, __x), never true enforcement
const on a methodPython has no way to promise a method won't mutate self
virtualPython always resolves methods at runtime — there's nothing to opt into
overridePython has no compile step to check the claim against
= 0 (pure virtual)Python's @abstractmethod from the abc module does the same job, just as a library feature instead of a language keyword
Virtual destructors / object slicingPython has no manual delete, no value-type copying of objects — references make both non-issues
ℹ️This Wraps Up the C++ Side of the Four Pillars

Everything from here — SOLID, then the design patterns themselves — will keep using this same C++ vocabulary: virtual, override, private/protected, initializer lists, smart pointers. Nothing new syntactically will show up without being introduced properly, but this page is the one to return to if a line ever looks unfamiliar.

⚠️🧠 Check Yourself Before Moving On

In your own words — what's the difference between virtual, override, and = 0? And why does a base class need a virtual destructor if it already has other virtual methods?