Struct vs. Class
The content is gathered from chatgpt

Struct vs. Class #

struct and class are interchangeable except for defaults:

  • struct members and base classes are public by default.
  • class members and base classes are private by default.
  • Otherwise they behave identically (constructors, inheritance, virtuals, templates, etc.).
  • Style: prefer struct for plain-data / public interfaces and class for encapsulation.

The snippet shows the default member and inheritance access differences and how to make them explicit.

// cpp
#include <iostream>

struct S {
  int x;           // public by default
};

class C {
  int x;           // private by default
public:
  C(int v) : x(v) {}
  int get() const { return x; }
};

struct Base {};
struct SDerived : Base { /* public inheritance by default */ };
class CDerived : Base { /* private inheritance by default */ };

int main() {
  S s{1};
  std::cout << s.x << "\n"; // OK: public

  C c(2);
  std::cout << c.get() << "\n"; // access via public method

  // SDerived sd; // behaves like public inheritance
  // CDerived cd; // behaves like private inheritance

  return 0;
}

Public inheritance #

Public inheritance means the derived type preserves the base type’s public interface and models an “is-a” relationship. Concretely:

  • Members: public and protected members of the base remain public and protected in the derived class.
  • Polymorphism: you can use a Derived* or Derived& where a Base*/Base& is expected, and virtual functions work through base pointers.
  • Semantics: expresses that Derived “is a” Base.
  • struct makes members public by default;

Example:

// C++
struct Base {
  virtual void f() = 0;    // public in Base
protected:
  int p;                   // protected in Base
};

struct PublicDerived : public Base {
  void f() override { p = 1; } // p still protected, f still public
};

struct PrivateDerived : private Base {
  void f() override {}  // Base::f becomes private to users of PrivateDerived
};

void takeBase(Base* b) { b->f(); }

int main() {
  PublicDerived pd;
  takeBase(&pd);        // OK: public inheritance allows substitution
  // PrivateDerived pr;
  // takeBase(&pr);     // ERROR: cannot convert due to private inheritance
}

Private inheritance #

Private inheritance means a derived class inherits implementation from a base class but hides the base’s public/protected interface from outside code. It is used to express “implemented in terms of” rather than “is-a”.

Key points:

  1. Public and protected members of the base become private in the derived class (outside users cannot access them).
  2. Automatic conversion from Derived* to Base* is not allowed for outside code (the base subobject is privately accessible). Only members and friends of Derived can do that conversion.
  3. Virtual dispatch still works if you have a Base* to the Derived object, but external code cannot get a Base* from a Derived* when inheritance is private.
  4. You can selectively expose base methods via using or by writing forwarding wrappers.

Example:

  • Shows private inheritance, attempt to pass Derived to a function taking Base* fails outside.
  • Derived can call Base members internally.
  • using Base::foo; under public: exposes foo to users.
// cpp
#include <iostream>

struct Base {
  virtual ~Base() = default;
  void foo() { std::cout << "Base::foo\n"; }
  virtual void v() { std::cout << "Base::v\n"; }
};

struct Derived : private Base {   // private inheritance
public:
  // expose foo() as public in Derived
  using Base::foo;

  // override virtual (still valid)
  void v() override { std::cout << "Derived::v\n"; }

  void callBase() {
    // Derived code can access Base members
    Base::foo();
  }
};

void takesBase(Base* b) {
  b->v();
}

int main() {
  Derived d;
  d.foo();         // OK: exposed via using-declaration
  d.callBase();    // OK: Derived can access Base::foo internally

  // takesBase(&d); // ERROR: cannot convert Derived* to Base* here because
                   // inheritance is private

  // But inside Derived (or its friends) conversion is allowed:
  // static_cast<Base*>(&d) is allowed only in Derived/friend scope.
  return 0;
}