Language[lang]

Inheritance in C++: base and derived classes

Section
Language [lang]
Standard
C++17
Compilers
MSVC 19.44, Clang 19.1 (MSVC STL), GCC 16.2, Clang 23.1 (libc++)
Published
Length
7 min read
In this answer7 clauses
  1. 1What does a derived class inherit?
  2. 2How do you call the base class constructor?
  3. 3How do you override a function in a derived class?
  4. 4Why does a base class need a virtual destructor?
  5. 5What is object slicing?
  6. 6What are the types of inheritance in C++?
  7. 7When should you use inheritance in C++?

Abstract

Inheritance in C++ lets a derived class take on the members of a base class: class Dog : public Animal gives Dog everything Animal has, plus its own members. Dog can use the public and protected members of Animal, calls the Animal constructor from its initializer list, and replaces one of its functions only when Animal declares that function virtual; mark the replacement override. Give such a base a virtual destructor, and pass objects by reference so they are not sliced.

A box at the top holding two blue hatched cells, and two larger boxes below joined to it by lines ending in an arrow. Each lower box holds the same two blue cells, then black cells of its own.
Figure 1 — A derived class contains its base and adds to it.

Inheritance in C++ lets one class take on the members of another. class Dog : public Animal makes Dog a derived class: it has everything Animal, the base class, has, plus what it adds. Use it when the new class is a kind of the old one. To let a derived class replace a base function, the base declares it virtual.

[Example 1:
#include <iostream>
#include <string>

class Animal {
public:
    std::string name;

    void eat() const {
        std::cout << name << " is eating\n";
    }
};

class Dog : public Animal {
public:
    void bark() const {
        std::cout << name << " says woof\n";
    }
};

int main() {
    Dog dog;
    dog.name = "Rex";  // a member Dog got from Animal
    dog.eat();         // inherited from Animal
    dog.bark();        // Dog's own
}

— end example]

It printed:

[Example 2:
Rex is eating
Rex says woof

— end example]

The samples are standard C++17, built with MSVC 19.44, Clang 19.1 (MSVC STL), GCC 16.2 and Clang 23.1 (libc++), and all four printed the same; Since When lists the compiler releases for each standard.

What does a derived class inherit?

A derived class inherits every member of its base except the constructors, but it can use only the public and protected ones; the base’s private members are part of every Dog, out of reach of its functions. A protected member is one the class and the classes derived from it can use, and outside code cannot.

Animal, the base class, is a box listing name and eat(). Below, a larger Dog box, the derived class, holds an identical shaded Animal part, then bark(). An arrow from Dog to Animal says a Dog is an Animal.
Figure 2 — A Dog has the whole Animal part, every member an Animal has, plus the members Dog adds.

Here Animal has one data member of each kind, and Dog uses the first two:

[Example 3:
class Animal {
public:
    std::string name;  // anyone can use it

    void eat() {
        energy += 5;
        ++meals;
    }
    int meals_eaten() const { return meals; }

protected:
    int energy = 10;   // Animal and the classes derived from it

private:
    int meals = 0;     // Animal's own functions only
};

class Dog : public Animal {
public:
    void fetch() {
        energy -= 3;   // protected: Dog may use it
        std::cout << name << " fetched, energy " << energy << '\n';
    }
};

— end example]

After eat() and fetch() on Rex, it printed Rex fetched, energy 12 and meals: 1: the private count is read through meals_eaten(), a public function of Animal. Reading meals inside Dog does not compile:

[Example 4:
    void fetch() {
        energy -= 3;   // OK: protected
        std::cout << name << " has eaten " << meals  // error: private
                  << " meals\n";
    }

— end example]

All four refused it; Clang said 'meals' is a private member of 'Animal'.

A member of Animal that is Usable inside Dog Usable from outside, on a Dog
public yes yes, with public inheritance
protected yes no
private no no

The public after the colon keeps the base’s public members public for code that uses a Dog (derived classes). Leave it out when the derived class is declared with class, and the inheritance is private ([class.access.base]): the public and protected members of Animal become private in Dog.

[Example 5:
class Dog : Animal {  // no "public": a class inherits privately
public:
    void bark() const {
        std::cout << name << " says woof\n";  // OK inside Dog
    }
};

int main() {
    Dog dog;
    dog.bark();  // OK: Dog's own
    dog.eat();   // error: eat() is private in Dog
}

— end example]

MSVC said:

[Example 6:
error C2247: 'Animal::eat' not accessible because 'Dog' uses 'private' to inherit from 'Animal'

— end example]

How do you call the base class constructor?

Call it in the derived constructor’s initializer list, the part after the colon: Dog(const std::string& name) : Animal(name). The base part is built first, then the derived part, and destruction runs in reverse (destructors):

[Example 7:
class Animal {
public:
    Animal(const std::string& name) : name(name) {
        std::cout << "Animal(" << name << ")\n";
    }
    ~Animal() { std::cout << "~Animal\n"; }

protected:
    std::string name;
};

class Dog : public Animal {
public:
    Dog(const std::string& name) : Animal(name) {
        std::cout << "Dog(" << name << ")\n";
    }
    ~Dog() { std::cout << "~Dog\n"; }
};

int main() {
    Dog dog("Rex");
    std::cout << "using the dog\n";
}

— end example]

All four printed:

[Example 8:
Animal(Rex)
Dog(Rex)
using the dog
~Dog
~Animal

— end example]

Leave the call out and the compiler calls Animal(), the default constructor; this Animal has none, so the Dog constructor does not compile (MSVC: error C2512: 'Animal': no appropriate default constructor available).

The ~Dog() and ~Animal() functions are destructors, which run when an object is destroyed. How a C++ constructor’s initializer list works covers the list itself.

How do you override a function in a derived class?

Declare the function virtual in the base, and write override after it in the derived class. A call through an Animal& or an Animal* then runs the version of the object’s real class, and override asks the compiler to check that the function really replaces a virtual one.

[Example 9:
class Animal {
public:
    virtual ~Animal() = default;
    virtual std::string speak() const { return "..."; }
};

class Dog : public Animal {
public:
    std::string speak() const override { return "Woof"; }
};

class Cat : public Animal {
public:
    std::string speak() const override { return "Meow"; }
};

void introduce(const Animal& animal) {
    std::cout << animal.speak() << '\n';
}

int main() {
    Dog dog;
    Cat cat;
    introduce(dog);  // Woof
    introduce(cat);  // Meow
}

— end example]

It printed Woof, then Meow: an Animal& can refer to a Dog or a Cat, because each is an Animal, and virtual makes the call reach their own speak(). The first line of Animal is a virtual destructor, which the next section explains.

Remove virtual and override and the same program printed ... twice: introduce() ran Animal::speak for both, and only dog.speak(), called on a Dog directly, printed Woof. Dog::speak now hides the base’s function instead of overriding it, and none of the four compilers warned about the hiding.

override, added in C++11 (Since When’s C++11 table), is the check you get instead: a function marked override that overrides nothing does not compile (override specifier). An override must match the base’s name, parameters and const, and this one drops the const:

[Example 10:
class Dog : public Animal {
public:
    // error: Animal::speak is const, this one is not
    std::string speak() override { return "Woof"; }
};

— end example]

GCC said:

[Example 11:
error: 'std::string Dog::speak()' marked 'override', but does not override

— end example]

How a virtual function call is resolved, with pure virtual functions and abstract classes, has its own answer.

Why does a base class need a virtual destructor?

When a derived object is deleted through a pointer to its base, as a std::unique_ptr<Animal> holding a Dog does, only a virtual destructor in the base makes the Dog part’s destructor run. Without one, the deletion is undefined behavior: the standard places no requirement on what the program then does ([expr.delete]).

[Example 12:
class Animal {
public:
    virtual ~Animal() { std::cout << "~Animal\n"; }
};

class Dog : public Animal {
public:
    ~Dog() { std::cout << "~Dog\n"; }
};

int main() {
    {
        std::unique_ptr<Animal> pet = std::make_unique<Dog>();
    }  // pet goes out of scope and deletes the Dog
    std::cout << "done\n";
}

— end example]

All four printed ~Dog, ~Animal, then done. The C++ Core Guidelines give the rule as C.35: a base class destructor should be either public and virtual, or protected and non-virtual. With nothing to clean up, virtual ~Animal() = default;, as in the override example, is enough.

What is object slicing?

Object slicing happens when you copy a derived object into a variable of the base type: the copy keeps only the Animal part, so its speak() is the base’s.

[Example 13:
Animal copy = dog;        // copies only the Animal part
std::cout << copy.speak() << '\n';  // ...

const Animal& ref = dog;  // refers to the whole Dog
std::cout << ref.speak() << '\n';   // Woof

— end example]

With the virtual speak() of the override example, it printed ..., then Woof. No compiler warned that the copy slices; the checker clang-tidy does, with its cppcoreguidelines-slicing check. Pass objects by reference or pointer, as introduce(const Animal&) does, to keep the whole object.

What are the types of inheritance in C++?

Tutorials name the types of inheritance by how many base and derived classes take part:

Type Shape Example
Single one base, one derived class Dog : public Animal
Multilevel a chain: a derived class is the base of another Puppy : public Dog
Hierarchical several classes derive from one base Dog and Cat, both from Animal
Multiple one class has several bases Dog : public Animal, public Pet

Some lists add hybrid inheritance, a mix of these. The access word before the base is a separate choice: public, protected and private inheritance decide what the base’s public and protected members become in the derived class.

When should you use inheritance in C++?

Inherit when the new class is a kind of the base, and hold a member when it has one instead: a Dog is an Animal, but a Car has an Engine, so Car holds an Engine member. Before you write the colon:

  • Write public after the colon.
  • Give a base class with virtual functions a virtual destructor.
  • Write override, without virtual, on every override (Core Guidelines C.128).
  • Pass objects of a base type by reference or pointer, not by value.

The Language section has the other answers on classes and the core language.

Questions

Does C++ support multiple inheritance?

Yes. A class can list several base classes, separated by commas, as in class Dog : public Animal, public Pet, and it inherits the members of each. The base constructors run in the order the bases are listed, left to right. Keep it for special cases: two bases that share a base of their own raise the diamond problem.

What is the difference between public, protected and private inheritance?

The access word before the base decides what the base’s members become in the derived class. With public inheritance, public members stay public and protected ones stay protected; with protected inheritance both become protected; with private inheritance both become private. The base’s private members stay out of the derived class’s reach whichever word you use, unless the base declares it a friend.

Are constructors inherited in C++?

Not by default: a derived class calls a base constructor from its own initializer list, as Dog(const std::string& name) : Animal(name) does. Since C++11, the line using Animal::Animal; inside Dog lets the constructors of Animal build a Dog, and a Dog constructor with the same parameters hides the inherited one.

What is the default inheritance of a class in C++?

Private, when the derived class is declared with class: class Dog : Animal inherits privately, so the public members of Animal become private in Dog and dog.eat() from outside does not compile. When the derived class is declared with struct, the default is public. Write public after the colon either way.

What is the diamond problem?

The diamond problem appears when two base classes share a base of their own: a class derived from both gets two copies of that shared base, one through each. Virtual inheritance is the fix: when both bases inherit the shared one as a virtual base (class Dog : virtual public Animal), the class at the bottom holds a single copy.

Can a struct inherit from a class in C++?

Yes, and a class can inherit from a struct. The two keywords differ only in their defaults, public for a struct and private for a class, for members and for inheritance alike. The default inheritance comes from the derived class’s keyword, so struct Dog : Animal inherits publicly.

Read next

  1. C++ virtual functions: override, pure virtual and the trapsLanguage · Miriam Castellanos · written for C++11
  2. C++ try catch: how to throw and catch exceptionsLanguage · Miriam Castellanos · written for C++17
  3. C++ enum and enum class: declare, convert, switchLanguage · Miriam Castellanos · written for C++11