目录
多级继承
在 多级继承 中,一个类继承另一个类,而这个类又继承另一个类,形成继承链。
#include <iostream>
using namespace std;
class A
{
public:
void showA(){ cout<<"A类"<<endl;}
};
class B:public A
{
public:
void showB(){ cout<<"B类"<<endl;}
};
class C : public B
{ // C 同时继承 A 和 B
public:
void showC() { cout << "C类" << endl; }
};
int main()
{
C obj;
obj.showA(); // ✅ 来自 A
obj.showB(); // ✅ 来自 B
obj.showC(); // ✅ C 自己的成员
}
class A //基类
class B : public A //派生类
class C : public B //派派生类
构造执行顺序: A -> B -> C
析构执行顺序: ~C -> ~B -> ~A
多继承
在 多继承 中,一个类可以同时继承多个基类,从多个类获取特性和功能。
语法
class 子类的名字:public 父类1,public 父类2 //公有继承
{
}
#include <iostream>
using namespace std;
class A {
public:
void showA() { cout << "A 类" << endl; }
};
class B {
public:
void showB() { cout << "B 类" << endl; }
};
class C : public A, public B { // C 同时继承 A 和 B
public:
void showC() { cout << "C 类" << endl; }
};
int main() {
C obj;
obj.showA(); // ✅ 来自 A
obj.showB(); // ✅ 来自 B
obj.showC(); // ✅ C 自己的成员
}
多继承的构造/析构函数调用
构造函数调用顺序:多个父类从左到右调用
析构函数调用顺序:多个父类从右到左调用
----------------------------------------
class C : public A, public B{} //💥💥先调用a后调用b的构造函数
class C : public B, public A{} //💥💥先调用b后调用a的构造函数
多继承+带参数构造函数
正常来说是系统调用默认的构造函数,如果基类构造函数需要参数,应该在派生类的初始化列表中显式调用它们。
#include <iostream>
using namespace std;
class A
{
public:
A(int x) {cout << "A的构造函数,x = " << x <<endl;}
A() {cout << "A的无参构造函数" << endl;}
~A() {cout << "A的析构函数" << endl;}
};
class B
{
public:
B(int y) {cout << "B的构造函数,y = " << y <<endl;}
B() {cout << "B的无参构造函数" << endl;}
~B() {cout << "B的析构函数" << endl;}
};
class C : public A, public B
{
public:
//✅写法1:部分指定,部分默认
//C():A(1){cout << "C的无参构造函数" << endl;}//c无参构造指定A的构造函数,B的使用默认的
//C():B(2){cout << "C的无参构造函数" << endl;}//c无参构造指定B的构造函数,A的使用默认的
//✅写法2:全部指定,顺序随便写,最终编译器按照继承的时候顺序从左到右调用
C():A(1),B(2){cout << "C的无参构造函数" << endl;}
~C() {cout << "C的析构函数" << endl;}
};
int main()
{
C c;
return 0;
}
多继承参数列表初始化
#include <iostream>
using namespace std;
class A
{
public:
A(int _x):a(_x) {cout << "x = " << x <<endl;}
private:
int x;
};
class B
{
public:
B(int _y):y(_y) {cout << "y = " << y <<endl;}
private:
int y;
};
class C : public A, public B
{
public:
C(int _x,int _y,int _z):A(_x),B(_y),z(_z){cout << "z = " << z << endl;}
private:
int z;
};
int main()
{
C c(1,2,3);
return 0;
}
多继承子类和父类同名方法
#include <iostream>
using namespace std;
class A
{
public:
void show(){cout << "A" << endl;}
};
class B
{
public:
void show(){cout << "B" << endl;}
};
class C : public A, public B
{
public:
void show(){cout << "C" << endl;}
};
int main()
{
C c;
c.show(); // ✅由于和父类方法同名了,因此会隐藏父类方法,改成调用自己的方法
c.A::show(); // ✅调用父类A的方法
c.B::show(); // ✅调用父类B的方法
return 0;
}
多继承的问题
菱形继承结构
菱形继承(Diamond Inheritance) 是 C++ 多继承 时遇到的经典问题。当一个类 同时从两个基类继承,而这两个基类又继承自同一个祖先类 时,就会形成 菱形结构,导致 数据冗余和二义性。
菱形继承的问题
问题 1:🚨二义性(Ambiguity)
为什么?
回顾之前的知识点,子类继承父类时,在子类的地址空间会有父类的地址空间副本,多重继承也是如此。
解决方法
显示指定使用哪个副本
Leo l1;
//l1.eat();// ❌ 编译错误,x 存在二义性!
l1.Catkind::eat(); //✅我要使用猫科动物创建的Animal副本来调用eat
l1.Manmual::eat(); //✅我要使用哺乳动物创建的Animal副本来调用eat
问题 2:🚨 数据冗余
观察构造函数调用的情况
#include <iostream>
using namespace std;
class Animal
{
public:
Animal()
{
cout<<"Animal无参构造"<<endl;
}
void eat()
{
cout<<"Animal动物吃"<<endl;
}
};
class Catkind:public Animal
{
public:
Catkind()
{
cout<<"猫科动物无参构造"<<endl;
}
private:
int age;
};
class Manmual:public Animal
{
public:
Manmual()
{
cout<<"哺乳动物无参构造"<<endl;
}
private:
double weight;
};
class Leo:public Catkind,public Manmual
{
public:
Leo()
{
cout<<"豹子无参构造"<<endl;
}
};
int main()
{
//观察构造函数的调用情况
Leo l1;
return 0;
}
如何解决? ✅使用虚继承
⭐(重点)virtual 虚方法
1.当C++ 使用了virtual 虚方法时,系统就会产生一个虚表,用于存储所有的虚方法 (虚函数,虚继承)。
2.当虚表中含有相同的方法时,会产生一个覆盖的特性。
3.当一个类中包含一个虚函数时,会产生一个虚表指针,指向系统的虚表。
4.虚表指针永远在类,最开始的地址!
虚继承
功能:解决菱形继承中的二义性与数据冗余问题!
语法
class 子类:virtual public 父类
class 子类:public virtual 父类
{
};
virtual和public的次序无关紧要
示例:使用虚继承彻底解决菱形继承的问题
#include <iostream>
using namespace std;
//虚基类
class Animal
{
public:
Animal()
{
cout<<"Animal无参构造"<<endl;
}
void eat()
{
cout<<"Animal动物吃"<<endl;
}
};
//猫科动物虚继承动物
class Catkind:virtual public Animal
{
public:
Catkind()
{
cout<<"猫科动物无参构造"<<endl;
}
private:
int age;
};
//哺乳动物虚继承动物
class Manmual:virtual public Animal
{
public:
Manmual()
{
cout<<"哺乳动物无参构造"<<endl;
}
private:
double weight;
};
class Leo:public Catkind,public Manmual
{
public:
Leo()
{
cout<<"豹子无参构造"<<endl;
}
};
int main()
{
//观察构造函数的调用情况
Leo l1;
//调用间接父类Animal的eat方法
l1.eat();
return 0;
}
虚基类表
在 C++ 虚继承 中,编译器使用“虚基类表(VBT,Virtual Base Table)”和“虚基类指针(VBP,Virtual Base Pointer)”来管理虚基类的共享实例。
虚基类指针
在虚继承的类中,每个对象内部会多出一个 虚基类指针(VBP),用于指向虚基类表(VBT)。
示例:证明虚继承后子类地址空间多出一个指针,专门用于指向虚基类表
#include <iostream>
using namespace std;
class Animal
{
public:
Animal()
{
cout<<"Animal无参构造"<<endl;
}
void eat()
{
cout<<"Animal动物吃"<<endl;
}
};
//猫科动物虚继承动物
class Catkind:virtual public Animal
{
public:
Catkind()
{
cout<<"猫科动物无参构造"<<endl;
}
private:
int age;
};
//哺乳动物虚继承动物
class Manmual:virtual public Animal
{
public:
Manmual()
{
cout<<"哺乳动物无参构造"<<endl;
}
private:
double weight;
};
class Leo:public Catkind,public Manmual
{
public:
Leo()
{
cout<<"豹子无参构造"<<endl;
}
};
int main()
{
//求子类的大小--》证明子类的地址空间中多了一个指针
cout<<"猫科的大小: "<<sizeof(Catkind)<<endl;
cout<<"哺乳的大小: "<<sizeof(Manmual)<<endl;
cout<<"豹子的大小: "<<sizeof(Leo)<<endl;
return 0;
}