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
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.

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.
#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:
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.

Here Animal has one data member of each kind, and Dog uses the first two:
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:
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.
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:
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::. The base part is built first, then the derived part, and destruction runs in reverse (destructors):
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:
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.
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:
class Dog : public Animal {
public:
// error: Animal::speak is const, this one is not
std::string speak() override { return "Woof"; }
};— end example]
GCC said:
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:: 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]).
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.
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
publicafter the colon. - Give a base class with virtual functions a virtual destructor.
- Write
override, withoutvirtual, 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.
Sources
- Derived classescppreference.com
- override specifiercppreference.com
- virtual function specifiercppreference.com
- Destructorscppreference.com
- Using-declaration (inheriting constructors)cppreference.com
- [class.access.base] Accessibility of base classes and base class membersC++ standard working draft
- [expr.delete] DeleteC++ standard working draft
- C++ Core Guidelines: C.35 and C.128C++ Core Guidelines