当前位置: 首页 > news >正文

C++继承基础总结

 引言

在编写多个类时,类之间可能会存在多个相同的成员变量,导致代码冗余度过高,C++继承的出现,使得我们可以在已有类的基础上构建新类,从而实现代码复用与结构扩展。

一、继承的基本概念

继承是指子类(派生类)继承父类(基类)的属性和方法,使得子类具有父类的功能,同时也可以扩展新的功能。通过继承,我们可以:

  • 重用现有代码
  • 建立类之间的层次关系
  • 扩展现有类的功能
  • 实现"是一种"(is-a)的关系

基本语法如下:

class Base
{
public:void Print(){cout << "class Base" << endl;}
};class Derived :public Base //子类声明所继承的基类
{};void test()
{Derived d;d.Print(); // 可以直接使用基类成员函数
}

二、继承方式

C++ 支持三种继承方式:publicprotectedprivate它们影响了基类成员在派生类中的访问权限:

总的来说:除了基类的私有成员无论何种方式被继承均无法访问,其余在派生类中的访问权限均取基类和派生类继承方式中访问权限最小者。public>protected>private

2.1公有继承

2.2保护继承

2.3保护继承

三、基类与派生类的构造与析构

构造顺序:先构造基类,再构造派生类

析构顺序:先析构派生类,再析构基类

四、派生类中初始化基类

五、派生类重定义基类中的成员函数

派生类可以重新定义(重写)基类中的成员函数,这与多态中的虚函数覆盖不同。当派生类定义了与基类同名的函数时,基类的函数会被"隐藏"。

class Base
{
public:Base(int x = 0) :base_value(x) {}void show(){cout << "base_value : " << base_value << endl;}
private:int base_value;
};class Derived : public Base
{
public:Derived(int x = 0) :Base(x), Derived_value(x) {} void show() //重定义基类中的成员函数{Base::show(); //调用基类的show函数cout << "Derived_value : " << Derived_value << endl;}
private:int Derived_value;
};void test3()
{Derived d(10);d.show(); //调用派生类的show函数
}

重定义基类成员函数时需要注意以下几点:

  1. 名称隐藏规则:派生类中的同名函数会隐藏基类中的所有同名函数,无论参数列表是否相同
  2. 作用域解析访问:可以使用作用域解析运算符::显式访问被隐藏的基类函数
  3. 与虚函数覆盖的区别:这种重定义不是多态行为,而是简单的名称隐藏
  4. 参数不同时的注意事项:即使参数列表不同,派生类的函数仍会隐藏基类的同名函数

六、多重继承

C++支持多重继承,一个基类可以有多个派生类,多个基类可以有一个共同的派生类:

声明一个派生类属于多个基类语法:

class A
{};class B
{};class C : public A, public B //基类之间用逗号隔开
{};

七、菱形继承问题

7.1什么是菱形继承

菱形继承(Diamond Inheritance)是多重继承中的一个常见问题,其结构如下所示:

       A                           在这个结构中:
    /      \                          类A是基类
 B         C                       类B和类C都继承自类A
    \      /                          类D同时继承自类B和类C
      D

7.2菱形继承带来的问题

二义性问题:当D想访问A中成员是,编译器会不知道应该走B这条路径来访问还是走C这条路径来访问

数据重复问题:D中会包含俩份来自A的数据成员,一份来自B,一份来自C,这样不仅会浪费内存,还会导致数据不一致。

7.3解决方案

法一:明确调用路径

class A  
{  
public:  void show()  {  cout << "Class A" << endl;  }  
};  class B : public A  
{  
public:  void showB()  {  cout << "Class B" << endl;  }  
};  class C : public A  
{  
public:  void showC()  {  cout << "Class C" << endl;  }  
};  class D : public B, public C  
{  
public:  void showD()  {  cout << "Class D" << endl;  }  
};  void test4()  
{  D d;  d.showD();  d.B::show(); // 明确走B路径访问A中成员 d.C::show(); // 明确走C路径访问A中成员 
}

法二:虚继承解决方案

虚继承的作用是让所有的派生类共享一个基类子对象,通过使得中间类都加上virtual关键字,声明中间类B、C虚继承基类A,让B、C共享一个A的副本,保证最终的派生类D中只包含一份基类的成员:

class A  
{  
public:  void show()  {  cout << "Class A" << endl;  }  
};  class B : virtual public A  
{  
public:  void showB()  {  cout << "Class B" << endl;  }  
};  class C : virtual public A  
{  
public:  void showC()  {  cout << "Class C" << endl;  }  
};  class D : public B, public C  
{  
public:  void showD()  {  cout << "Class D" << endl;  }  
};  void test4()  
{  D d;  d.showD();  d.show();   
}

虚继承的特点

  1. 构造顺序改变
    • 在虚继承中,首先构造虚基类,然后按照常规顺序构造其他类
    • 最终派生类需要负责虚基类的初始化,即使它与虚基类之间隔了多层继承
  2. 虚基类表
    • 编译器通过特殊的"虚基类表"机制实现虚继承
    • 这会增加一些运行时开销和对象大小
  3. 虚基类成员访问
    • 虚基类的成员在整个继承层次中只存在一份
    • 可以直接通过最终派生类对象访问虚基类的成员

实际工程中建议尽量避免多重继承,特别是菱形继承结构。

相关文章:

  • 【AI面试准备】电商购物车AI测试设计与实施
  • Javase 基础入门 —— 07 接口
  • P1434 [SHOI2002] 滑雪
  • Redis持久化:
  • 如何实现一个虚拟dom
  • 随机变量数字特征
  • 【Bootstrap V4系列】学习入门教程之 组件-按钮(Buttons)
  • [更新完毕]2025东三省C题深圳杯C题数学建模挑战赛数模思路代码文章教学: 分布式能源接入配电网的风险分析
  • 【科研绘图系列】R语言绘制世界地图(map plot)
  • 自己部署后端,浏览器显示久久未响应
  • 【第十六届蓝桥杯省赛】比赛心得与经验分享(PythonA 组)
  • 欺骗单页应用(SPA)渲染隐藏路由 -- trouble at the spa b01lersCTF
  • 【现代深度学习技术】现代循环神经网络04:双向循环神经网络
  • 【AI论文】DeepCritic:使用大型语言模型进行有意识的批判
  • 【深度学习的灵魂】图片布局生成模型LayoutPrompt(2)·布局序列化模块
  • Linux电源管理(5)_Hibernate和Sleep功能介绍
  • Centos9 安装 RocketMQ5
  • Windows 中使用dockers创建指定java web 为镜像和运行容器
  • 深度学习系统学习系列【2】之人工神经网络(ANN)
  • 长江学者答辩ppt美化_特聘教授_校企联聘学者_青年长江学者PPT案例模板
  • 无人机穿越大理崇圣寺千年古塔时“炸机”,当地:肇事者已找到,将被追责
  • 退休11年后,71岁四川厅官杨家卷被查
  • 江西浮梁县县长张汉坤被查,此前已有4个月无公开活动
  • 五一上海楼市热闹开局:售楼处全员到岗,热门楼盘连续触发积分
  • 岳伟华任北京大学第六医院院长,陆林院士卸任
  • 四人自驾游宣恩因酒店爆满无处住宿,求助文旅局后住进局长家