inheritance
inheritance
Inheritance is one of the three fundamental pillars of object-oriented programming
There are special relationships between certain classes, as shown in the following image:

We noticed that when defining these classes, the members of subclasses not only share the common properties of the superclass but also have their own unique characteristics.
At this point, we can consider using the inheritance technique to reduce duplicate code.
Inheritance Basics
For example, we see many websites with common headers, common footers, and even common sidebars, where only the central content differs.
Next, let's implement the web content using both ordinary and inheritance approaches, and explore the significance and benefits of inheritance.
Standard Implementation:
//Java页面
class Java
{
public:
void header()
{
cout << "首页、公开课、登录、注册...(公共头部)" << endl;
}
void footer()
{
cout << "帮助中心、交流合作、站内地图...(公共底部)" << endl;
}
void left()
{
cout << "Java,Python,C++...(公共分类列表)" << endl;
}
void content()
{
cout << "JAVA学科视频" << endl;
}
};
//Python页面
class Python
{
public:
void header()
{
cout << "首页、公开课、登录、注册...(公共头部)" << endl;
}
void footer()
{
cout << "帮助中心、交流合作、站内地图...(公共底部)" << endl;
}
void left()
{
cout << "Java,Python,C++...(公共分类列表)" << endl;
}
void content()
{
cout << "Python学科视频" << endl;
}
};
//C++页面
class CPP
{
public:
void header()
{
cout << "首页、公开课、登录、注册...(公共头部)" << endl;
}
void footer()
{
cout << "帮助中心、交流合作、站内地图...(公共底部)" << endl;
}
void left()
{
cout << "Java,Python,C++...(公共分类列表)" << endl;
}
void content()
{
cout << "C++学科视频" << endl;
}
};
void test01()
{
//Java页面
cout << "Java下载视频页面如下: " << endl;
Java ja;
ja.header();
ja.footer();
ja.left();
ja.content();
cout << "--------------------" << endl;
//Python页面
cout << "Python下载视频页面如下: " << endl;
Python py;
py.header();
py.footer();
py.left();
py.content();
cout << "--------------------" << endl;
//C++页面
cout << "C++下载视频页面如下: " << endl;
CPP cp;
cp.header();
cp.footer();
cp.left();
cp.content();
}
int main() {
test01();
return 0;
}
Run/Observation results: After running, it will print the common header, common footer, common categories, and respective content for the Java, Python, and C++ download pages.
Implementation of Inheritance:
//公共页面
class BasePage
{
public:
void header()
{
cout << "首页、公开课、登录、注册...(公共头部)" << endl;
}
void footer()
{
cout << "帮助中心、交流合作、站内地图...(公共底部)" << endl;
}
void left()
{
cout << "Java,Python,C++...(公共分类列表)" << endl;
}
};
//Java页面
class Java : public BasePage
{
public:
void content()
{
cout << "JAVA学科视频" << endl;
}
};
//Python页面
class Python : public BasePage
{
public:
void content()
{
cout << "Python学科视频" << endl;
}
};
//C++页面
class CPP : public BasePage
{
public:
void content()
{
cout << "C++学科视频" << endl;
}
};
void test01()
{
//Java页面
cout << "Java下载视频页面如下: " << endl;
Java ja;
ja.header();
ja.footer();
ja.left();
ja.content();
cout << "--------------------" << endl;
//Python页面
cout << "Python下载视频页面如下: " << endl;
Python py;
py.header();
py.footer();
py.left();
py.content();
cout << "--------------------" << endl;
//C++页面
cout << "C++下载视频页面如下: " << endl;
CPP cp;
cp.header();
cp.footer();
cp.left();
cp.content();
}
int main() {
test01();
return 0;
}
Run/Observation Results: Output after running is identical to the previous page example, but the common parts are now reused through the base class. Focus on observing the reduction in duplicate code brought by inheritance.
Summary:
Advantages of inheritance: It reduces redundant code
class A : public B;
A class is called a subclass or derived class.
Class B is referred to as the parent class or base class.
Members of a derived class consist of two main parts:
One category is inherited from the base class, and the other category consists of members added by the class itself.
Inherited from a base class to represent common characteristics, while new members are added to reflect unique characteristics.
Inheritance method
Syntax of inheritance: class 子类 : 继承方式 父类
There are three types of inheritance methods:
- public inheritance
- Protected inheritance
- private inheritance

Example:
class Base1
{
public:
int m_A;
protected:
int m_B;
private:
int m_C;
};
//公共继承
class Son1 :public Base1
{
public:
void func()
{
m_A; //可访问 public权限
m_B; //可访问 protected权限
//m_C; //不可访问
}
};
void myClass()
{
Son1 s1;
s1.m_A; //其他类只能访问到公共权限
}
//保护继承
class Base2
{
public:
int m_A;
protected:
int m_B;
private:
int m_C;
};
class Son2:protected Base2
{
public:
void func()
{
m_A; //可访问 protected权限
m_B; //可访问 protected权限
//m_C; //不可访问
}
};
void myClass2()
{
Son2 s;
//s.m_A; //不可访问
}
//私有继承
class Base3
{
public:
int m_A;
protected:
int m_B;
private:
int m_C;
};
class Son3:private Base3
{
public:
void func()
{
m_A; //可访问 private权限
m_B; //可访问 private权限
//m_C; //不可访问
}
};
class GrandSon3 :public Son3
{
public:
void func()
{
//Son3是私有继承,所以继承Son3的属性在GrandSon3中都无法访问到
//m_A;
//m_B;
//m_C;
}
};
Run/Observation Results: This section is syntax definition-focused, typically requiring compilation together with the calling code. Pay attention to the definition method and usage location.
Object Model in Inheritance
Answer: The members inherited from the parent class that are directly accessible and contained within a subclass object include:
- Public and protected data members (variables).
- Public and protected member functions (methods).
However, private members from the parent class are still inherited and exist in the subclass object's memory layout, but they are not directly accessible by the subclass's own code. They can only be accessed indirectly through inherited public or protected methods from the parent class.
In summary:
- Accessible in subclass: Public and protected members.
- Present but hidden: Private members.
This distinction is fundamental in object-oriented programming for encapsulation and data hiding.
Example:
class Base
{
public:
int m_A;
protected:
int m_B;
private:
int m_C; //私有成员只是被隐藏了,但是还是会继承下去
};
//公共继承
class Son :public Base
{
public:
int m_D;
};
void test01()
{
cout << "sizeof Son = " << sizeof(Son) << endl;
}
int main() {
test01();
return 0;
}
Running/Observation Results: After running, the corresponding content will be printed according to the output statements. The variable values can be inferred based on the order of initialization, assignment, and function calls.
Using tools to inspect:

After opening the tool window, navigate to the drive letter of the current CPP file.
Then enter: cl /d1 reportSingleClassLayout the class name to view the file name it belongs to
The effect is as shown below:

Conclusion: Private members in a parent class are also inherited by subclasses; they are simply hidden by the compiler and become inaccessible.
Inheritance: Constructor and Destructor Order
When a subclass inherits from a parent class, creating a subclass object will also invoke the constructor of the parent class.
Question: What is the order of constructor and destructor calls in parent and child classes?
Answer: When creating an instance of a subclass, the parent class constructor is called first, followed by the subclass constructor. During destruction, the subclass destructor is called first, then the parent class destructor. This order ensures that dependencies are initialized properly and resources are cleaned up safely.
Example:
class Base
{
public:
Base()
{
cout << "Base构造函数!" << endl;
}
~Base()
{
cout << "Base析构函数!" << endl;
}
};
class Son : public Base
{
public:
Son()
{
cout << "Son构造函数!" << endl;
}
~Son()
{
cout << "Son析构函数!" << endl;
}
};
void test01()
{
//继承中 先调用父类构造函数,再调用子类构造函数,析构顺序与构造相反
Son s;
}
int main() {
test01();
return 0;
}
Running/Observation Results: After running, the corresponding content will be printed according to the output statements. The variable values can be inferred based on the order of initialization, assignment, and function calls.
Summary: In inheritance, the parent class constructor is called first, followed by the child class constructor, while destructors are invoked in the reverse order.
Handling Inherited Members with the Same Name
Question: When subclasses and superclasses have members with the same name, how can we access the identically named data in the subclass or superclass through an object of the subclass?
- Access subclass members with the same name directly access it.
- Accessing same-named members of a parent class requires scope qualification.
Example:
class Base {
public:
Base()
{
m_A = 100;
}
void func()
{
cout << "Base - func()调用" << endl;
}
void func(int a)
{
cout << "Base - func(int a)调用" << endl;
}
public:
int m_A;
};
class Son : public Base {
public:
Son()
{
m_A = 200;
}
//当子类与父类拥有同名的成员函数,子类会隐藏父类中所有版本的同名成员函数
//如果想访问父类中被隐藏的同名成员函数,需要加父类的作用域
void func()
{
cout << "Son - func()调用" << endl;
}
public:
int m_A;
};
void test01()
{
Son s;
cout << "Son下的m_A = " << s.m_A << endl;
cout << "Base下的m_A = " << s.Base::m_A << endl;
s.func();
s.Base::func();
s.Base::func(10);
}
int main() {
test01();
return EXIT_SUCCESS;
}
Running/Observation Results: After running, the corresponding content will be printed according to the output statements. The variable values can be inferred based on the order of initialization, assignment, and function calls.
Summary:
- Subclass objects can directly access members with the same name within the subclass.
- A subclass object can access a parent class member with the same name by using scope resolution.
- When a derived class has member functions with the same name as those in the base class, the derived class hides the same-named member functions in the base class. Adding a scope qualifier allows access to the same-named function in the base class.
Handling of static members with the same name inherited in a class
Question: How to access static members with the same name in a subclass object in inheritance?
When static and non-static members share the same name, they are handled the same way.
- Access subclass members with the same name directly access it.
- Accessing same-named members of a parent class requires scope qualification.
Example:
class Base {
public:
static void func()
{
cout << "Base - static void func()" << endl;
}
static void func(int a)
{
cout << "Base - static void func(int a)" << endl;
}
static int m_A;
};
int Base::m_A = 100;
class Son : public Base {
public:
static void func()
{
cout << "Son - static void func()" << endl;
}
static int m_A;
};
int Son::m_A = 200;
//同名成员属性
void test01()
{
//通过对象访问
cout << "通过对象访问: " << endl;
Son s;
cout << "Son 下 m_A = " << s.m_A << endl;
cout << "Base 下 m_A = " << s.Base::m_A << endl;
//通过类名访问
cout << "通过类名访问: " << endl;
cout << "Son 下 m_A = " << Son::m_A << endl;
cout << "Base 下 m_A = " << Son::Base::m_A << endl;
}
//同名成员函数
void test02()
{
//通过对象访问
cout << "通过对象访问: " << endl;
Son s;
s.func();
s.Base::func();
cout << "通过类名访问: " << endl;
Son::func();
Son::Base::func();
//出现同名,子类会隐藏掉父类中所有同名成员函数,需要加作作用域访问
Son::Base::func(100);
}
int main() {
//test01();
test02();
return 0;
}
Running/Observation Results: After running, the corresponding content will be printed according to the output statements. The variable values can be inferred based on the order of initialization, assignment, and function calls.
Summary: Static members are handled in the same way as non-static members, with the only difference being that they can be accessed in two ways: through an object or via the class name.
Multiple inheritance syntax
C++ allows a class to inherit from multiple classes
Grammar: class 子类 :继承方式 父类1 , 继承方式 父类2...
Multiple inheritance can cause conflicts when parent classes have identically named members, requiring the use of the scope resolution operator to differentiate them.
In practical C++ development, multiple inheritance is not recommended.
Example:
class Base1 {
public:
Base1()
{
m_A = 100;
}
public:
int m_A;
};
class Base2 {
public:
Base2()
{
m_A = 200; //开始是m_B 不会出问题,但是改为mA就会出现不明确
}
public:
int m_A;
};
//语法:class 子类:继承方式 父类1 ,继承方式 父类2
class Son : public Base2, public Base1
{
public:
Son()
{
m_C = 300;
m_D = 400;
}
public:
int m_C;
int m_D;
};
//多继承容易产生成员同名的情况
//通过使用类名作用域可以区分调用哪一个基类的成员
void test01()
{
Son s;
cout << "sizeof Son = " << sizeof(s) << endl;
cout << s.Base1::m_A << endl;
cout << s.Base2::m_A << endl;
}
int main() {
test01();
return 0;
}
Running/Observation Results: After running, the corresponding content will be printed according to the output statements. The variable values can be inferred based on the order of initialization, assignment, and function calls.
Summary: In multiple inheritance, if parent classes have same-named members, the child class must use scope resolution when accessing them.
Diamond Inheritance
Diamond Inheritance Concept:
Two derived classes inherit from the same base class.
There goes another class that inherits from two derived classes.
This type of inheritance is called diamond inheritance, or diamond-shaped inheritance.
A classic case of diamond inheritance:

Diamond inheritance problem:
Sheep inherits data from Animal, and Camel also inherits data from Animal. When Grass Mud Horse uses the data, ambiguity arises.- Llama inherits data from animal twice, but we should be clear that we only need one copy of this data.
Example:
class Animal
{
public:
int m_Age;
};
//继承前加virtual关键字后,变为虚继承
//此时公共的父类Animal称为虚基类
class Sheep : virtual public Animal {};
class Tuo : virtual public Animal {};
class SheepTuo : public Sheep, public Tuo {};
void test01()
{
SheepTuo st;
st.Sheep::m_Age = 100;
st.Tuo::m_Age = 200;
cout << "st.Sheep::m_Age = " << st.Sheep::m_Age << endl;
cout << "st.Tuo::m_Age = " << st.Tuo::m_Age << endl;
cout << "st.m_Age = " << st.m_Age << endl;
}
int main() {
test01();
return 0;
}
Running/Observation Results: After running, the corresponding content will be printed according to the output statements. The variable values can be inferred based on the order of initialization, assignment, and function calls.
Summary:
- The primary issue with diamond inheritance is that subclasses inherit two copies of the same data, leading to wasteful resource usage and serving no meaningful purpose.
- Using virtual inheritance can solve the diamond inheritance problem.