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++支持多重继承的编程方式
- 多重继承容易带来问题
- 可能出现“同一个对象的地址不同”的情况
- 虚继承可以解决数据冗余的问题
- 虚继承使得架构设计可能出现问题