【C++_10】多重继承与虚继承(定义方法、构造和析构函顺序、二义性、重复继承、虚拟继承)

本文详细介绍了C++中的多重继承概念,包括派生类的构造和析构顺序,以及如何处理可能出现的二义性问题。通过实例展示了如何在派生类中调用基类的方法和成员,并探讨了重复继承可能导致的问题。此外,还讲解了虚拟继承的作用,即解决多继承时的二义性,确保公共基类在派生类中只有一份拷贝。最后,给出了虚拟继承构造顺序的例子。
摘要由CSDN通过智能技术生成

多重继承的定义方法

  • 如果在定义一个派生类时,该派生类继承了2个或2个以上基类的特征,那么这种继承关系就称为多重继承。
  • 多重继承派生类定义格式:
    class <派生类名>:<继承方式1> <基类名1>,<继承方式2> 
    <基类名2>,{
          <派生类新增加的数据成员>
          <派生类新增加的成员函数>
    };
  • 多重继承举例 :
#include <iostream>
#include <string>
using namespace std;

class CA //基类CA
{
public:
    void setA(int x) { a = x; }
    void printA() { cout << a << endl; }

private:
    int a;
};
class CB //基类CB
{
public:
    void setB(int x) { b = x; }
    void printB() { cout << b << endl; }

private:
    int b;
};
class CC : public CA, public CB //派生类,多重继承
{
public:
    void setC(int x, int y, int z) //调用了setA,setB
    {
        setA(x);
        setB(y);
        c = z;
    }
    void printC() { cout << c << endl; }

private:
    int c;
};

int main()
{
    CC obj;            //派生类对象
    obj.setA(1);       //调用setA,设置a=1
    obj.printA();      //输出a=1
    obj.setC(2, 3, 4); //调用setC,设置a=2,b=3,c=4
    obj.printC();      //输出c=4
    obj.setB(5);       //调用setB,设置b=5
    obj.printB();      //输出b=5
}

多重继承的构造、析构顺序

  • 多重继承派生类的构造函数,实现对继承成员初始化,对新加成员初始化。

  • 定义形式(类似单继承)

    • 当基类构造函数不带参数时,不必显式指明调用基类构造函数;
    • 当基类构造函数带参数时,必须显式指明调用基类构造函数;并由派生类构造函数的形式参数为被调用的基类构造函数提供实参。
  • 多重继承派生类构造函数的一般定义格式为:

    派生类名::派生类名(基类1形参,基类2形参,… 派生类形参):基类名1(参数), 基类名2(参数), ...基类名n(参数)
      {
           派生类成员初始化赋值语句;
      }
  • 执行顺序:

    • 先执行基类的构造函数,再调用派生类构造函数中新加入部分;
    • 当有多个基类构造函数要执行时,按照派生类定义时基类出现的次序(从左到右)执行(而不是派生类构造函数定义时基类构造函数出现的次序)

  • 多重继承派生类的析构函数,用于撤消派生类对象所占用的空间
  • 定义形式:由于析构函数都不带参数,故不必显式指明如何调用基类的析构函数。
  • 执行次序:与构造函数相反

  • 多重继承的构造、析构顺序练习:

    例:建立如下的类继承结构:

    1. 定义学生类CStudent,其属性(保护类型)有:
      姓名、学号;
      2)定义教师类CTeacher,属性有:姓名、职称;
    2. 从CStudent和CTeacher类共同派生出在职研究生类
      CGradOnWork,添加属性:研究方向;
      2、分别定义以上类的构造函数、输出函数print及其它函数。
      3、在主函数中定义各种类的对象,并测试之。
#include <iostream>
#include <string>
using namespace std;

class CStudent
{
protected:
	string no;
	string name;

public:
	CStudent()
	{
		cout << this << " CStudent cons default" << endl;
	}
	CStudent(string nval, string noval) : name(nval), no(noval)
	{

		cout << this << " CStudent cons para" << endl;
	}
	void print()
	{
		cout << name << " " << no << endl;
	}
	~CStudent()
	{
		cout << this << " CStudent des" << endl;
	}
};

class CTeacher
{
protected:
	string title;
	string name;

public:
	CTeacher()
	{
		cout << this << " CTeacher cons default" << endl;
	}
	CTeacher(string nval, string tval) : name(nval), title(tval)
	{

		cout << this << " CTeacher cons para" << endl;
	}
	void print()
	{
		cout << name << " " << title << endl;
	}
	~CTeacher()
	{
		cout << this << " CTeacher des" << endl;
	}
};

//多重继承
class CGradOnWork : public CTeacher, public CStudent //多重继承派生类定义
{
	string research;

public:
	CGradOnWork() //无参构造函数
	{
		cout << this << " CGradOnWork cons default" << endl;
	}
	CGradOnWork(string name, string no, string title, string re) : CStudent(name, no), CTeacher(name, title), research(re) //有参构造函数,初始化参数列表
	{																													   //需要继承过来的学生类和教师类所有变量值
		cout << this << " CGradOnWork cons para" << endl;
	}
	/*CGradOnWork(string name, string no, string title, string re) : research(re)   //调基类的无参构造,因为没有传入参数
	{
		CTeacher::name = name; //基类的初始化
		this->no = no;
		cout << this << " CGradOnWork cons para" << endl;
	}*/
	void print()
	{
		cout << CStudent::name << " " << no << " " << title << " " << research << " " << endl; //方法1:使用作用域运算符解决二义性问题
	}

	~CGradOnWork() //析构函数
	{
		cout << this << " CGradOnWork des" << endl;
	}
};

int main()
{
	CGradOnWork g("liming", "2022", "lecture", "computer"); //初始化
	g.print();

	// cout << sizeof(CGradOnWork) << " " << sizeof(CStudent) << " " << sizeof(CTeacher) << endl;
}

/*输出:构造和析构顺序(先构造基类,按照定义顺序)。

0x7ff7b8606378 CTeacher cons para
0x7ff7b86063a8 CStudent cons para
0x7ff7b8606378 CGradOnWork cons para
liming 2022 lecture computer
0x7ff7b8606378 CGradOnWork des
0x7ff7b86063a8 CStudent des
0x7ff7b8606378 CTeacher des
*/

多重继承中的二义性

  • 多重继承中,派生类的基类中有2个或2个以上基类含有相同名字的成员,在派生类中该名字产生了二义性,使编译程序无法判断派生类对象在调用该名字时应调用哪个基类中的版本。
  • 解决名字冲突的办法有两个: 使用作用域运算符 或者 重定义有名字冲突的成员
    • 方法1:使用作用域运算符::,指明所使用名字是哪个基类的作用域。
    • 方法2:重定义有冲突的成员
      • “重定义”的含义:函数原型相同,函数体不同;
      • 执行原则:编译调用派生类对象中的成员函数时,先查找派生类对象中有没有定义该成员,若有定义,就调用该成员,若找不到,再到其祖先类中去寻找

  • 重复继承:一个派生类2次或2次以上继承同一个基类

  • C++关于重复继承的基本规则

    • 一个类必须完全定义后才可以作为基类--无法直接或间接让一个类继承自己(不能递归)

    • 不允许一个派生类直接重复继承一个基类两次

    • 不允许一个基类又是直接基类又是间接基类

    • 如果所有派生类都是间接,那么一个类可以从某个祖先类派生出2次甚至多次:允许通过间接形式重复继承某个祖先类(D 通过 B, C 重复继承祖先类 A)

      image-20220503191810927
  • 问题:当不加特别声明时,重复继承将出现二义性问题(D中仍然有两个备份,所以还是需要通过作用域对变量进行限定)。如下所示:

image-20220503184335007
  • 重复继承二义性解决方法
    • 方法1:在派生类中使用作用域运算符::标明该成员的作用域(从哪个基类继承过来)
    • 方法2:通过虚拟继承,使某个公共基类的成员在其派生类中只产生一个拷贝。

虚拟继承

  • 为什么需要虚拟继承
    • 为了解决多继承时可能发生的对同一基类继承多次而产生的二义性问题,使某个公共基类的成员在其派生类中只产生一个拷贝
    • 可在从基类派生新的类时将这个基类用virtual关键字说明为虚基类
    • 例如:class CBase1 : virtual public Cbase
  • 虚基类作用
    • 如某个基类被声明为虚基类时,那么在被重复继承时,在派生类对象实例中只存储一个副本
    • 若不声明为虚基类,就会出现多个副本

虚拟继承的构造顺序

  • 带有虚基类的派生类的构造函数

    • 先执行虚基类的构造函数,再执行不是虚基类的基类的构造函数,最后执行构造函数中新加入部分;
    • 若有多个虚基类时,依派生类定义时,虚基类出现次序从左至右地执行;
    • 当有多个非虚基类时,也依派生类定义时,基类出现次序,从左至右地执行。
  • 虚拟继承这部分理解以下的代码即可:

    例:建立如下的类继承结构:

    1. 定义一个人员类CPeople,其属性(保护类型)有:
      姓名;
    2. 从CPeople类派生出学生类CStudent,添加属性:学号;
    3. 从CPeople类再派生出教师类CTeacher,添加属性:
      职称;
    4. 从CStudent和CTeacher类共同派生出在职研究生类
      CGradOnWork,添加属性:研究方向;
      2、分别定义以上类的构造函数、输出函数print及其他函数。
      3、在主函数中定义各种类的对象,并测试之。
    #include <iostream>
    #include <string>
    using namespace std;
    
    class CPerson //将学生和教师类中共有的name成员提取出来,
    {
    protected:
    	string name;
    
    public:
    	CPerson()
    	{
    		name = "wangliu";
    		cout << this << " CPerson cons default" << endl;
    	}
    	CPerson(string nval) : name(nval)
    	{
    		cout << this << " CPerson cons para" << endl;
    	}
    	CPerson(const CPerson &rhs) : name(rhs.name) //拷贝构造,用已有对象初始化新对象
    	{
    		cout << this << " CPerson cons copy" << endl;
    	}
    	void print()
    	{
    		cout << name << endl;
    	}
    	~CPerson()
    	{
    		cout << this << " CPerson des" << endl;
    	}
    };
    class CStudent : virtual public CPerson //虚拟继承,这样name就只有一个备份了
    {
    protected:
    	string no;
    
    public:
    	CStudent()
    	{
    		cout << this << " CStudent cons default" << endl;
    	}
    	CStudent(string name, string noval) : CPerson(name), no(noval)
    	{
    		cout << this << " CStudent cons para" << endl;
    	}
    	CStudent(const CStudent &rhs) : CPerson((CPerson)rhs), no(rhs.no) //注意这里拷贝构造的初始化方法
    	{
    		cout << this << " CStudent cons copy" << endl;
    	}
    	void print()
    	{
    		cout << name << " " << no << endl;
    	}
    	~CStudent()
    	{
    		cout << this << " CStudent des" << endl;
    	}
    };
    
    class CTeacher : virtual public CPerson //虚拟继承
    {
    protected:
    	string title;
    
    public:
    	CTeacher()
    	{
    		cout << this << " CTeacher cons default" << endl;
    	}
    	CTeacher(string name, string tval) : CPerson(name), title(tval) //通过person类进行初始化
    	{
    		cout << this << " CTeacher cons para" << endl;
    	}
    	void print()
    	{
    		cout << name << " " << title << endl;
    	}
    	~CTeacher()
    	{
    		cout << this << " CTeacher des" << endl;
    	}
    };
    
    class CGradOnWork : public CTeacher, public CStudent //多重继承
    {
    	string research;
    
    public:
    	CGradOnWork()
    	{
    		cout << this << " CGradOnWork cons default" << endl;
    	}
    	CGradOnWork(string name, string no, string title, string re) : CPerson(name), CTeacher(name, title), CStudent(name, no), research(re) //虚基类要单独构建
    	{
    		cout << this << " CGradOnWork cons para" << endl;
    	}
    	CGradOnWork(const CGradOnWork &rhs) : CPerson(rhs.name), CTeacher((CTeacher)rhs), CStudent((CStudent)rhs), research(rhs.research) //不用单独再赋值了
    	{
    		cout << this << " CGradOnWork cons copy" << endl;
    	}
    	void print()
    	{
    		cout << name << " " << no << " " << title << " " << research << " " << endl;
    	}
    	~CGradOnWork()
    	{
    		cout << this << " CGradOnWork des" << endl;
    	}
    };
    
    int main()
    {
    	CGradOnWork g("liming", "2022", "lecture", "computer"); //研究生类有参调用
    	cout << "-----------------" << endl;
    	/*输出
    	0x7ff7b70343d8 CPerson cons para
    	0x7ff7b7034380 CTeacher cons para 	//不会再第二次调用CPerson构建了!
    	0x7ff7b70343a0 CStudent cons para	//不会再第二次调用CPerson构建了!
    	0x7ff7b7034380 CGradOnWork cons para //跟CTeacher共享空间
    	-----------------
    	*/
    
    	CGradOnWork g2(g); //研究生类拷贝构造
    	cout << "-----------------" << endl;
    	/*输出 // CGradOnWork(const CGradOnWork &rhs) : CPerson(rhs.name), CTeacher((CTeacher)rhs), CStudent((CStudent)rhs), research(rhs.research)
    	0x7ff7b70342f8 CPerson cons para	//CPerson带参构造
    	0x7ff7b7034170 CPerson cons copy	//CPerson拷贝构造
    	0x7ff7b7034150 CTeacher des			//CTeacher析构
    	0x7ff7b7034170 CPerson des			//CPerson拷贝构造-- 析构
    	0x7ff7b7034098 CPerson cons copy	//CPerson 拷贝构造1
    	0x7ff7b7034138 CPerson cons copy	//CPerson 拷贝构造2
    	0x7ff7b7034098 CPerson des			//CPerson 拷贝构造1--析构
    	0x7ff7b7034118 CStudent cons copy	//CStudents 拷贝构造1
    	0x7ff7b70342c0 CStudent cons copy	//CStudents 拷贝构造2
    	0x7ff7b7034118 CStudent des			//CStudents 拷贝构造1--析构
    	0x7ff7b7034138 CPerson des			//CPerson 拷贝构造2--析构
    	0x7ff7b70342a0 CGradOnWork cons copy//CGradOnWork拷贝构造
    	-----------------
    	*/
    
    	CStudent s("liming", "2021"); //学生类有参构造
    	cout << "-----------------" << endl;
    	/*输出
    	0x7ff7b7034288 CPerson cons para
    	0x7ff7b7034268 CStudent cons para 	//还是先构建虚基类,再构建自己
    	-----------------
    	*/
    
    	g.print(); //打印研究生类,输出name,no,title,research
    	cout << "-----------------" << endl;
    	//输出:liming 2022 lecture computer
    
    	s.print(); //打印学生类,输出name,no
    	cout << "-----------------" << endl;
    	//输出:liming 2021
    
    	cout << sizeof(CGradOnWork) << " " << sizeof(CStudent) << " " << sizeof(CTeacher) << " " << sizeof(CPerson) << endl; //输出地址大小
    	cout << "-----------------" << endl;
    	//输出:112 56 56 24
    
    	//开始进行析构
    	/*输出
    	0x7ff7b7034268 CStudent des
    	0x7ff7b7034288 CPerson des
    	0x7ff7b70342a0 CGradOnWork des
    	0x7ff7b70342c0 CStudent des
    	0x7ff7b70342a0 CTeacher des
    	0x7ff7b70342f8 CPerson des
    	0x7ff7b7034380 CGradOnWork des
    	0x7ff7b70343a0 CStudent des
    	0x7ff7b7034380 CTeacher des
    	0x7ff7b70343d8 CPerson des
    	*/
    }
    

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ferry_xie

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值