C++继承机制:从基础到菱形继承全解析
目录
C++ 继承的基本概念
继承是面向对象编程(OOP)的核心特性之一,允许一个类(派生类也可以叫子类)基于另一个类(基类也可以是父类)构建,继承其成员变量和成员函数。C++ 支持多种继承方式,包括单继承、多继承和虚继承。
继承类型对比表
| 继承类型 | 基类 public 成员在派生类中的访问权限 | 基类 protected 成员在派生类中的访问权限 | 基类 private 成员在派生类中的访问权限 |
|---|---|---|---|
| 公有继承 | public | protected | 不可直接访问 |
| 保护继承 | protected | protected | 不可直接访问 |
| 私有继承 | private | private | 不可直接访问 |
这个表可以不用记以最小权限为访问权限: public > protected > private ,其中class的继承可以不写private,class默认就是private,则struct默认就是public。如果是private派生类无法访问基类,
protected派生类可以访问但是类外访问不了,public就都可以访问。
class person
{
public:
person(const std::string& name1,const std::string& ID,const std::string& add )
:name(name1),
ID_card(ID),
address(add)
{
}
protected:
std::string name;
std::string ID_card;
std::string address;
};
class my_form :public person
{
public:
my_form(const std::string& name1, const std::string& ID, const std::string& add, const std::string& work1)
:person(name1,ID,add),
work(work1)
{
}
void Date()
{
std::cout <<"名字:" << name << std::endl;
std::cout << "身份证:" << ID_card << std::endl;
std::cout << "地址:" << address << std::endl;
std::cout << "工作:" << work << std::endl;
}
private:
std::string work;
};
int main()
{
my_form i("张三", "12345", "广东", "学生");
i.Date();
return 0;
}
继承类模板
类模板和继承——这种继承耦合性高,没有组合好,我们可以创建可以操作多种数据类型的基类,并通过继承机制扩展这些类的功能。这在实现通用数据结构(如链表、树等)或编写与类型无关的算法时特别有用。
#include<vector>
template<class T>
class stack :protected std::vector<T>// stack和vector的关系,既符合is-a,也符合has-a
{
public:
void push(const T& x)
{
std::vector<T>::push_back(x);
}
void pop()
{
std::vector<T>::pop_back();
}
T top()
{
return *(std::vector<T>::end()-1);
}
size_t size()
{
return std::vector<T>::size();
}
bool empty()
{
return std::vector<T>:: empty();
}
};
继承中的作⽤域
隐藏规则:
1. 在继承体系中基类和派⽣类都有独⽴的作⽤域。
2. 派⽣类和基类中有同名成员,派⽣类成员将屏蔽基类对同名成员的直接访问,这种情况叫隐藏。 (在派⽣类成员函数中,可以使⽤基类::基类成员显⽰访问)
3. 需要注意的是如果是成员函数的隐藏,只需要函数名相同就构成隐藏。
4. 注意在实际中在继承体系⾥⾯最好不要定义同名的成员。
class A
{
public:
void f() { std::cout << "A::f()" << std::endl; }
int a;
};
class B : public A
{
public:
void f(int a) { std::cout << "B::f()" << std::endl; }
int a;
};
int main()
{
B b;
b.f(1);
b.A::f();
//b.f()这样子会编译报错
return 0;
}
基类和派⽣类间的转换
基类和派生类之间的转换主要有三种方式:指针转换、引用转换和对象拷贝。指针和引用转换是隐式的,并且只能访问派生类对象中的基类部分成员;对象拷贝会进行切片操作,只保留派生类对象中的基类部分成员。这些转换在多态性和继承机制中起着重要作用,但也需要注意切片可能带来的信息丢失问题。

派生类默认成员函数
这里的构造跟普通构造差不多,如果没有写构造就是编辑器默认构造,如果自己写了构造编辑器不生成构造,如果加了default就会产生默认构造,其中初始化列表也是一样的。


继承的基类(父类)必须要在自己类里面写默认构造,在子类写会报错,调用默认构造可以看上面代码。

继承与友元
基类友元无法访问派生类对象(还比于父亲的朋友不一定是我朋友),所以继承不了友元
class B;
class A
{
public:
friend void Print__AB(const A& a,const B& b);
protected:
int _a;
};
class B : public A
{
protected:
int _b;
};
void Print__AB(const A &a,const B& b)
{
std::cout << a._a << b._b << std::cout;//这里会编译器报错访问不了_b.
}
继承与静态成员
基类定义了static静态成员,则整个继承体系⾥⾯只有⼀个这样的成员。⽆论派⽣出多少个派⽣类,都 只有⼀个static成员实例。
class A
{
public:
void _apush1()
{
_a++;
}
protected:
static int _a;
};
int A::_a = 0;
class B : public A
{
public:
int a_size()
{
return _a;
}
protected:
int _b;
};
int main()
{
A y;
B x;
y._apush1();
x._apush1();
std::cout<<x.a_size()<<std::endl;//输出2
return 0;
}
多继承及其菱形继承问题
C++多继承及其菱形继承问题解析(无代码)
多继承核心概念
多继承是面向对象编程中的一种机制,允许一个派生类同时从多个基类继承属性和行为:
- 基本模型:派生类如同"组装体",整合多个基类的功能
- 内存布局:派生类对象包含所有基类的子对象(按继承顺序排列)
- 典型场景:设备控制器(继承输入接口+输出接口+电源管理)
菱形继承问题
当继承结构形成菱形时,引发经典问题:

核心矛盾:
-
数据冗余问题
- B和C各自包含A的完整副本
- D同时继承B和C → 包含两份A的副本
- 结果:相同数据重复存储,浪费内存
-
访问二义性问题
- 当D访问A的成员时:
- 编译器无法确定应通过B路径还是C路径访问
- 结果:编译错误(ambiguous access)
- 当D访问A的成员时:
解决方案:虚继承
通过virtual关键字重构继承关系:
class B : virtual public A {...};
class C : virtual public A {...};
class D : public B, public C {...};
虚继承的魔法:
-
共享基类机制
- B和C不再包含完整A副本
- 改为存储指向共享A的指针(虚基表指针)
-
派生类主导构造
- 最底层派生类(D)直接构造虚基类(A)
- 中间类(B/C)不触发A的构造
-
访问统一化
- 所有路径指向同一A实例
- 彻底消除二义性
IO库中应用

继承和组合
| 特性 | 继承(Inheritance) | 组合(Composition) |
|---|---|---|
| 关系语义 | "是一个"(is-a) | "有一个"(has-a) |
| 耦合度 | 紧耦合(父子绑定) | 松耦合(部件独立) |
| 生命周期 | 同生共死 | 自主控制 |
| 访问权限 | 直接访问基类protected成员 | 仅通过接口访问 |
| 典型类比 | 家族血脉传承 | 汽车组装零件 |
注意尽量使用组合(低耦合关联性低)比较好
更多推荐



所有评论(0)