C++深度解析(43)—被遗弃的多重继承(上)

1.C++中的多重继承

  • C++支持编写多重继承的代码 
    • 一个子类可以拥有多个父类
    • 子类拥有所有父类的成员变量
    • 子类继承所有父类的成员函数
    • 子类对象可以当作任意父类对象使用(父子类有赋值兼容性原则)
  • 多重继承的语法规则
class Derived : public BaseA,public BaseB,public BaseC{
  //````  
};
  • 多重继承的本质与单继承相同!多重继承容易带来问题,在其他语言已经被遗弃。

2.多重继承问题一

  • 通过多重继承得到的对象可以拥有“不同的地址!!!
  • 解决方案:
  • 原因分析(不同的父类指针指向子类中属于自己的那部分地址)
  • 编程实验:多重继承问题一
#include <iostream>

using namespace std;

class BaseA
{
	int ma;

public:
	BaseA(int a)
	{
		ma = a;
	}

	int getA()
	{
		return ma;
	}
};

class BaseB
{
	int mb;

public:
	BaseB(int b)
	{
		mb = b;
	}

	int getB()
	{
		return mb;
	}
};

// 多继承
class Derived : public BaseA, public BaseB
{
	int mc;

public:
	Derived(int a, int b, int c) :BaseA(a), BaseB(b)  // 初始化列表。
	{
		mc = c;
	}

	int getC()
	{
		return mc;
	}

	void print()
	{
		cout << "ma = " << getA() << ", "
			<< "mb = " << getB() << ", "
			<< "mc = " << mc << endl;
	}
};

int main()
{
	cout << "sizeof(Derived) = " << sizeof(Derived) << endl; // 每个类私有变量的字节数加在一起
	Derived d(1, 2, 3);

	d.print();

	cout << "d.getA() = " << d.getA() << endl;
	cout << "d.getB() = " << d.getB() << endl; // 用类的对象访问成员方法
	cout << "d.getC() = " << d.getC() << endl;

	cout << endl;

	BaseA *pa = &d;
	BaseB *pb = &d; // 注意以上两行,都是将对象d的地址赋值给一个指针,表面上看似pa指针应该等于pb指针,
	                // 但实际上不会这样,pa指向的是d对象中BaseA的子对象,而pb指向的是d对象中BaseB子对象的部分。(这两部分并未重合)
	cout << "pa->getA() = " << pa->getA() << endl;  // 1
	cout << "pb->getB() = " << pb->getB() << endl;  // 2

	cout << endl;

	void *paa = pa;
	void *pbb = pb;

	if (paa == pbb)
	{
		cout << "point to the same Object" << endl;
	}
	else
	{
		cout << "ERROR" << endl;
	}

	cout << "pa = " << pa << endl;
	cout << "pb = " << pb << endl;
	cout << "paa = " << paa << endl;
	cout << "pbb = " << pbb << endl;
		
	system("pause");
	
	return 0;
}
  • 运行结果

3.多重继承的问题二

  • 多重继承可能产生冗余的成员
  • eacher、Student都有People类所有成员变量成员函数,Doctor类同时继承Teacher类、Student类,不现实?

  • 编程实验:多重继承问题二
/*多重继承产生的问题2*/
#include <iostream>
 
using namespace std;
 
class People
{
    string m_name;
    int m_age;
public:
    People(string name, int age)
    {
        m_name = name;
        m_age = age;
    }
    void print()
    {
        cout << "Name = " << m_name << ", "
             << "Age = " << m_age << endl;
    }
};
 
class Teacher : public People
{
public:
    Teacher(string name, int age) : People(name, age)
    {
    }
};
 
class Student : public People
{
public:
    Student(string name, int age) : People(name, age)
    {
    }
};
 
class Doctor : public Teacher, public Student
{
public:
    Doctor(string name, int age) : Teacher(name + "1", age + 1), Student(name + "2", age + 2)
    {
    }
};
 
int main()
{
    Doctor d("Nyist", 30);
 
    //d.print();//error
 
    d.Teacher::print();//Name = Nyist1, Age = 31
    d.Student::print();//Name = Nyist2, Age = 32
 
    return 0;
}
  • 当多重继承关系出现闭合时将产生数据冗余的问题!!!! 
  • 解决方案:虚继承

  • 虚继承能够解决数据冗余问题
  • 中间层父类不用再关心顶层父类的初始化(但是中间层也要调用父类构造函数进行初始化。)
  • 最终子类必须直接调用顶层父类(以及中间父类)的构造函数
#include <iostream>
#include <string>

using namespace std;

class People
{
	string mName;
	int mAge;

public:
	People(string name, int age)  // 构造函数
	{
		mName = name;
		mAge = age;
	}

	void print()
	{
		cout << "Name = " << mName << ", "
			<< "Age = " << mAge << endl;
	}
};

// 先看看虚继承,然后再撤销虚继承,中间类采用虚继承
class Teacher : virtual public People
{
public:
	Teacher(string name, int age) : People(name, age)  // 构造函数,初始化列表
	{

	}
};

//中间类采用虚继承
class Student : virtual public People
{
public:
	Student(string name, int age) : People(name, age)
	{

	}

};
// 博士类(一个博士可能即是老师,又是学生),采用直接继承,如果中间层的Teacher和Student不采用虚继承的话,
// 那在Doctor类中将有来自People类的mName,mAge等成员变量各两份,出现数据冗余现象,而且在子类Doctor中,
// 如果直接mName = 1, 会出现二义性的错误,因为编译器不知道mName到底是来自Teacher的还是Student类的。
class Doctor : public Teacher, public Student
{
public:
	// 请注意,构造函数中的最后一个初始化People,也就是说采用虚继承的话,
	// 则最终的子类仍需调用顶层父类的构造函数,这也是虚继承的一大问题,因为在实际开发中,有时很难确定最顶层的父类。
	Doctor(string name, int age) :Teacher(name, age), Student(name, age), People(name, age)
	{

	}
};

// 各自私有属性是独立的
int main()
{
	Doctor d("SantaClaus", 25); // 先父类,在朋友,再自己
	
	d.print();

	system("pause");
	
	return 0;
}
  • 运行结果:
  • 依然存在的问题:当架构设计中需要继承时,无法确定使用直接继承还是虚继承!!

4.小结

  • C++支持多重继承的编程方式
  • 多重继承容易带来问题 
    • 可能出现“同一个对象的地址不同”的情况
    • 虚继承可以解决数据冗余的问题
    • 虚继承使得架构设计可能出现问题
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值