一、继承

继承性是面向对象程序设计的第二大特性,它允许在既有类的基础上创建新类,新类可以继承既有类的数据成员和成员函数,可以添加自己特有的数据成员和成员函数,还可以对既有类中的成员函数重新定义。利用类的继承和派生实现了更高层次的代码可重用性,符合现代软件开发的思想。

C++语言同时支持单一继承和多重继承。单一继承是指派生类只从一个基类继承而来;相应的,多重继承指派生类同时从两个或更多的基类继承而来。

1. 继承的特性:

(1)定义派生类关键字可以是class或者是struct,两者区别是:用class定义派生类,默认的继承方式是private,用struct定义派生类,默认的继承方式为public。新增加的成员默认属性也是class对应private属性,struct对应public属性。

(2)基类不能被派生类继承的两类函数是构造函数(默认构造、拷贝构造、移动构造)和析构函数。

实例:单一继承

#include"stdafx.h"
#include<iostream>
using namespace std;class Other
{public:Other(){cout<<"constructing Other class"<<endl;}~Other(){cout<<"destructing Other class"<<endl;}
};class Base
{public:Base(){cout<<"constructing Base class"<<endl;}~Base(){cout<<"destructing Base class"<<endl;}
};class Derive:public Base
{private:Other ot;
public:Derive(){cout<<"constructing Derive class"<<endl;}~Derive(){cout<<"destructing Derive class"<<endl;}
};int main()
{Derive d;return 0;
}

运行结果:

可以看到定义派生类对象时,构造函数的调用顺序:

  1. 先调用基类的构造函数
  2. 然后调用派生类对象成员所属类的构造函数(如果有对象成员)
  3. 最后调用派生类的构造函数

析构函数的调用顺序正好与构造函数调用顺序相反。

二、多重继承

1. 多重继承概述

多重继承:常规情况,一个类只有一个基类,而C++支持多重继承,即一个类可以继承自多个类。

人(Person)可以吃饭和睡觉,既可以是作家也可以是程序员,作家可以写文章,程序员可以写程序,

即是作家又是程序员的人能够写文章和写程序。

多重继承的优缺点:

多重继承的优点很明显,就是对象可以调用多个基类中的接口;

多重继承的缺点是什么呢?如果派生类所继承的多个基类有相同的基类,而派生类对象需要调用这个祖先类的接口方法,就会容易出现二义性。

对于二义性,通常有两个解决方案:

(1)加上全局符确定调用哪一份拷贝。

(2)使用虚拟继承,使得多重继承类只拥有基类类的一份拷贝。

2. 静态成员变量

在C++中(以及其他一些语言,如 C#,Java 等面向对象的语言中)类的成员变量被声明为static(称为静态成员变量),意味着它为该类的所有实例所共享,也就是说当某个类的实例修改了该静态成员变量,其修改值为该类的其它所有实例所见。

静态成员变量特性:

  1. 静态成员变量属于整个类所有;
  2. 静态成员的生命周期不依赖于任何对象(程序包运行的整个周期);
  3. 可以通过类名直接访问公有静态成员变量;
  4. 所有对象共享类的静态成员变量;
  5. 可以通过对象名访问公有静态成员变量;
  6. 在定义时直接通过static关键字修饰;
  7. 静态成员变量需要在类外单独分配空间;
  8. 静态成员变量在程序内部位于全局数据区(但是文件间无法共享)。

父类的static变量和函数在派生类中依然可用,但是受访问性控制(比如,父类的private域中的就不可访问),而且对static变量来说,派生类和父类中的static变量是共用空间的,这点在利用static变量进行引用计数的时候要特别注意。

static函数没有“虚函数”一说。因为static函数实际上是“加上了访问控制的全局函数”,全局函数哪来的什么虚函数?

派生类的friend函数可以访问派生类本身的一切变量,包括从父类继承下来的protected域中的变量。但是对父类来说,他并不是friend的。

3. 静态成员函数

  1. 静态成员函数是类中的特殊的成员函数;
  2. 静态成员函数没有隐藏的this指针;
  3. 静态成员函数可以通过类名直接访问;
  4. 静态成员函数可以通过对象访问;
  5. 静态成员函数只能直接访问静态成员变量(函数),而不能直接访问普通成员变量(函数)。

4. 派生类构造函数与析构函数

在定义一个派生类的对象时,在派生类中新增加的数据成员当然用派生类的构造函数初始化,但是对于从基类继承来的数据成员的初始化工作就必须由基类的构造函数完成,这就需要在派生类的构造函数中完成对基类构造函数的调用。同样,派生类的析构函数只能完成派生类中新增加数据成员的扫尾、清理工作,而从基类继承来的数据成员的扫尾工作也应有基类的析构函数完成。由于析构函数不能带参数,因此派生类的析构函数默认直接调用了基类的析构函数。

派生类构造函数与析构函数的特点:

(1)一般情况下,基类名后面的参数表中的实际参数来自前面派生类构造函数形式参数总表,当然也可能是与前面形式参数无关的常量;

(2)多层次继承中,每一个派生类只需要负责向直接基类的构造函数提供参数;如果一个基类有多个派生类,则每个派生类都要负责向该基类的构造函数提供参数。

定义派生类对象时,构造函数的调用顺序:

(1)先调用基类的构造函数

(2)然后调用派生类对象成员所属类的构造函数(如果有对象成员)

(3)最后调用派生类的构造函数

析构函数的调用顺序正好与构造函数调用顺序相反。

5. 从多个父类继承构造函数

在多重继承中,派生类有多个平行的基类,这些处于同一层次的基类构造函数的调用顺序,取决于声明派生类时所指定的各个基类的顺序,而与派生类构造函数的成员初始化列表中调用基类构造函数的顺序无关。

实例:多重继承

#include"stdafx.h"
#include<iostream>
using namespace std;class Grand
{int g;
public:Grand(int n):g(n){cout<<"Constructor of class Grand g="<<g<<endl;}~Grand(){cout<<"Destructor of class Grand"<<endl;}
};class Father:public Grand
{int f;
public:Father(int n1,int n2):Grand(n2),f(n1){cout<<"Constructor of class Father f="<<f<<endl;}~Father(){cout<<"Destructor of class Father"<<endl;}
};class Mother
{int m;
public:Mother(int n):m(n){cout<<"Constructor of class Mother m="<<m<<endl;}~Mother(){cout<<"Destructor of class Mother"<<endl;}
};class Son:public Father,public Mother
{int s;
public:Son(int n1,int n2,int n3,int n4):Mother(n2),Father(n3,n4),s(n1){cout<<"Constructor of class Son s="<<s<<endl;}~Son(){cout<<"Destructor of class Son"<<endl;}
};int main()
{Son s(1,2,3,4);return 0;
}

运行结果:

可以看到,与单一继承不同的是:在多重继承中,派生类有多个平行的基类,这些处于同一层次的基类构造函数的调用顺序,取决于声明派生类时所指定的各个基类的顺序,而与派生类构造函数的成员初始化列表中调用基类构造函数的顺序无关。

三、虚基类、虚继承(虚派生)

1、虚基类

如果一个派生类有多个直接基类,而这些直接基类又有一个共同的基类,则在最终的派生类中会保留该间接共同基类数据成员的多份同名成员。

在引用这些同名的成员时,必须在派生类对象名后增加直接基类名,以避免产生二义性,使其惟一地标识一个成员,如

c1.A::display( )。

在一个类中保留间接共同基类的多份同名成员,这种现象是人们不希望出现的。C++提供虚基类(virtual base class )的方法,使得在继承间接共同基类时只保留一份成员。

注意:

虚基类并不是在声明基类时声明的,而是在声明派生类时,指定继承方式时声明的。因为一个基类可以在生成一个派生类时作为虚基类,而在生成另一个派生类时不作为虚基类。

声明虚基类的一般形式为

class 派生类名: virtual 继承方式

基类名

经过这样的声明后,当基类通过多条派生路径被一个派生类继承时,该派生类只继承该基类一次。

需要注意: 为了保证虚基类在派生类中只继承一次,应当在该基类的所有直接派生类中声明为虚基类。否则仍然会出现对基类的多次继承。

如果在派生类B和C中将类A声明为虚基类,而在派生类D中没有将类A声明为虚基类,则在派生类E中,虽然从类B和C路径派生的部分只保留一份基类成员,但从类D路径派生的部分还保留一份基类成员。

虚基类的初始化如果在虚基类中定义了带参数的构造函数,而且没有定义默认构造函数,则在其所有派生类(包括直接派生或间接派生的派生类)中,通过构造函数的初始化表对虚基类进行初始化。

在最后的派生类中不仅要负责对其直接基类进行初始化,还要负责对虚基类初始化。C++编译系统只执行最后的派生类对虚基类的构造函数的调用,而忽略虚基类的其他派生类。

对虚基类的构造函数的调用,这就保证了虚基类的数据成员不会被多次初始化。

2、虚继承

虚继承是面向对象编程中的一种技术,是指一个指定的基类,在继承体系结构中,将其成员数据实例共享给也从这个基类型直接或间接派生的其它类。

1)D继承了B,C也就继承了两个虚基类指针

2)虚基类表存储的是,虚基类相对直接继承类的偏移(D并非是虚基类的直接继承类,B,C才是)

3)虚基类时,D直接初始化A类。如果D类有了子类,将由其子类初始化A类,即由最底层的派生类来初始化

4)初始化顺序:先初始化虚基类部分,然后再按照派生列表中出现的顺序来初始化其他类。

5)如果有多个虚基类,会按照派生列表中的直接基类来回追溯,看是否这些直接基类含有虚基类,追溯到哪个虚基类,就先构造哪个虚基类的子内容,析构顺序相反。
实例:

#include<iostream>
using namespace std;class A  //大小为4
{public:int a;
};
class B :virtual public A  //大小为12,变量a,b共8字节,虚基类表指针4
{public:int b;
};
class C :virtual public A //与B一样12
{public:int c;
};
class D :public B, public C //24,变量a,b,c,d共16,B的虚基类指针4,C的虚基类指针4
{public:int d;
};int main()
{A a;B b;C c;D d;cout << sizeof(a) << endl;cout << sizeof(b) << endl;cout << sizeof(c) << endl;cout << sizeof(d) << endl;system("pause");return 0;
}

运行结果分析:

C++继承的构造函数、多重继承、虚继承相关推荐

  1. 【C++】继承和派生、虚继承和虚基类、虚基类表和虚基类指针

    继承和派生.虚继承和虚基类.虚基类表和虚基类指针 继承和派生 继承概述 继承基本概念 派生类中的成员 继承的内容 派生类定义 派生类访问控制 对象构造和析构 对象构造和析构的调用顺序 继承中的构造和析 ...

  2. C++ Primer 5th笔记(chap 18 大型程序工具)构造函数与虚继承

    1. 继承体系中的每个类都可能在某个时刻成为" 最低层的派生类". 只要我们能创建虚基类的派生类对象, 该派生类的构造函数就必须初始化它的虚基类. Bear::Bear (std: ...

  3. 钻石问题(菱形继承问题) 和虚继承

    在C++中,什么叫做钻石问题(也可以叫菱形继承问题),怎么避免它? 下面的图表可以用来解释钻石问题. 假设我们有类B和类C,它们都继承了相同的类A.另外我们还有类D,类D通过多重继承机制继承了类B和类 ...

  4. 继承(下)----虚继承

    单继承&多继承 一个子类只有一个直接父类时称这种继承关系为单继承. 一个子类有两个或者两个以上的父类时称这种继承关系为多继承. 菱形继承 ---------特殊的多继承 有很大的缺点: 二义性 ...

  5. 多继承的二义性和虚继承(虚基类)

    一般来说,在派生类中对基类成员的访问是应该是唯一的.但是,由于在多继承的情况下,可能出现基类中某个成员的访问不唯一的情况,这称为对基类成员访问的二义性. 在多继承的情况下,通常有两种可能出现的二义性. ...

  6. 6.12C++:继承基类的构造函数、单继承的构造函数、多继承的构造函数、派生类复制构造函数、派生类的析构函数

    1 继承基类的构造函数 class A{public:A(){}; // A的构造函数 }; class B : public A{public:using A:A: } 2 单继承的构造函数 cla ...

  7. C++基础学习-26继承的构造函数、多重继承、虚继承

    目录 继承的构造函数 多重继承 1.多重继承的概念 2.静态成员变量 3.派生类构造函数与析构函数 4.从多个父类继承构造函数 类型转换 虚基类.虚继承(虚派生) 总结 继承的构造函数 C++语言同时 ...

  8. 多重继承与虚继承编程实验

    多重继承与虚继承编程实验 基本知识 多重继承 多重继承下的类作用域 虚继承 构造函数与虚继承 关于本程序 示例代码 Animal_virtual_baseVers.h virt-inherit.cpp ...

  9. c++primer 笔记03多重继承与虚继承

    18.3多重继承与虚继承 多个直接基类中产生派生类的能力,多重继承的派生类继承了所有父类的属性 18.3.1多重继承 派生类的派生列表中可以包含多个基类,但这些类不能是final的 class Zoo ...

  10. C++基本概念复习之二:多重继承、虚继承、纯虚函数(抽象类)

    一.多重继承: #include <iostream> using namespace std; class Horse { public: Horse(){cout<<&qu ...

最新文章

  1. 一个.java源文件中是否可以包括多个类
  2. Oracle ASM 翻译系列第十二弹:ASM Internal amdu - ASM Metadata Dump Utility
  3. C语言中嵌入正则表达式
  4. Chrome Console Cookie 控制台操作命令
  5. 使用 GB28181.Solution + ZLMediaKit + MediaServerUI 进行摄像头推流和播放
  6. 前端学习(2960):实现发送axios请求
  7. oracle负数怎么比较大小,输出负数【oracle学习吧】_百度贴吧
  8. 专访探真科技:云原生安全与业务迭代平衡术
  9. 12.PHP-FPM
  10. 中空格的asc码表_Excel怎么快速提取混合单元格中的中文、英文、数字?
  11. mac 蓝牙 串口调试 助手(工具)
  12. python利用PIL及openpyxl实现图片转为excel表格
  13. 考出面试者基本功的 10 个简单编程题
  14. 教你Word一键自动生成目录步骤
  15. maven报错cannot reconnect
  16. linux-使用screen后台运行命令,防止断网导致异常退出,命令没运行完成
  17. 字节跳动 Java 岗一二三面全经过分享
  18. 一起开心2020蓝桥寒假训练(二)7-6 彩虹瓶 (25分)用到栈,队列
  19. android 安卓GBA GBC NDS FC SFC 街机游戏模拟器源码
  20. JavaScript的逆袭

热门文章

  1. 矩阵分析与应用(19)
  2. java中git使用教程_Git—使用方法
  3. Vue 中数组常用方法的使用和示例
  4. powerShell 使用 chcp 65001 之后,还是显示乱码问题解决
  5. GAN介绍 - 为什么学习生成式模型?
  6. vbox通过u盘安装系统
  7. piaget读法_各名牌正确发音
  8. 欢迎使用CSDN-markdown编辑
  9. 前端如何开发 APP
  10. android输入日期格式,android – 当用户输入时,如何格式化信用卡到期日期(mm / yy)...