多态(Polymorphism)是面向对象编程的核心特性之一,它允许同一个接口表现出不同的行为。在C++中,多态分为两种主要类型:编译时多态和运行时多态。
多态的字面意思是"多种形态",在编程中指的是同一个操作作用于不同的对象时,可以有不同的解释和执行结果。多态提供了一种统一的接口来处理不同类型的对象。
编译时多态(Static Polymorphism)也称为静态多态,在编译期间就确定了具体调用哪个函数。主要包括:
函数重载(Function Overloading)
运算符重载(Operator Overloading)
模板(Templates)
函数模板特化
运行时多态(Dynamic Polymorphism)也称为动态多态,在运行时才确定具体调用哪个函数。主要通过虚函数实现。
基本虚函数
纯虚函数和抽象类
特性 | 编译时多态 | 运行时多态 |
---|---|---|
决定时机 | 编译期 | 运行期 |
实现方式 | 函数重载、运算符重载、模板 | 虚函数、继承 |
性能 | 高(无运行时开销) | 较低(虚函数调用开销) |
灵活性 | 低(编译时固定) | 高(运行时可变) |
内存开销 | 无额外开销 | 需要vtable和vptr |
类型安全 | 编译时检查 | 运行时检查 |
代码复用 | 通过模板实现 | 通过继承实现 |
编译时多态和运行时多态各有优势:
编译时多态提供了高性能和类型安全,适合性能敏感和泛型编程场景
运行时多态提供了灵活性和扩展性,适合需要动态行为的场景
虚函数表(Virtual Function Table, vtable)
虚函数表是一个函数指针数组,存储着类中所有虚函数的地址。每个包含虚函数的类都有一个对应的虚函数表。
虚指针(Virtual Pointer, vptr)
虚指针是每个对象中的一个隐藏成员,指向该对象所属类的虚函数表。
基本结构
xxxxxxxxxx
121class Base {
2public:
3 virtual void func1() { cout << "Base::func1" << endl; }
4 virtual void func2() { cout << "Base::func2" << endl; }
5 virtual ~Base() {}
6};
7
8class Derived : public Base {
9public:
10 void func1() override { cout << "Derived::func1" << endl; }
11 virtual void func3() { cout << "Derived::func3" << endl; }
12};
内存布局
xxxxxxxxxx
151Base对象内存布局:
2+-------------------+
3| vptr | <- 指向Base的vtable
4+-------------------+
5| 其他成员变量 |
6+-------------------+
7
8Derived对象内存布局:
9+-------------------+
10| vptr | <- 指向Derived的vtable
11+-------------------+
12| Base部分成员 |
13+-------------------+
14| Derived新增成员 |
15+-------------------+
虚函数表结构
xxxxxxxxxx
191Base的vtable:
2+-------------------+
3| &Base::func1 |
4+-------------------+
5| &Base::func2 |
6+-------------------+
7| &Base::~Base |
8+-------------------+
9
10Derived的vtable:
11+-------------------+
12| &Derived::func1 | <- 覆盖了Base::func1
13+-------------------+
14| &Base::func2 | <- 继承自Base
15+-------------------+
16| &Derived::~Derived| <- 即使派生类没有显式写出析构,其默认析构也会覆盖基类的虚析构函数
17+-------------------+
18| &Derived::func3 | <- 新增的虚函数
19+-------------------+
当通过基类指针调用虚函数时:
xxxxxxxxxx
21Base* ptr = new Derived();
2ptr->func1(); // 调用虚函数
调用过程:
获取vptr:从对象中读取虚指针
查找vtable:通过vptr找到对应的虚函数表
索引函数:根据函数在vtable中的索引找到函数地址
调用函数:通过函数指针调用实际的函数
在多重继承中,情况更加复杂:
xxxxxxxxxx
171class Base1 {
2public:
3 virtual void func1() {}
4 virtual ~Base1() {}
5};
6
7class Base2 {
8public:
9 virtual void func2() {}
10 virtual ~Base2() {}
11};
12
13class Derived : public Base1, public Base2 {
14public:
15 void func1() override {}
16 void func2() override {}
17};
xxxxxxxxxx
121Derived对象内存布局:
2+-------------------+
3| Base1 vptr | <- 指向Base1部分的vtable
4+-------------------+
5| Base1 成员 |
6+-------------------+
7| Base2 vptr | <- 指向Base2部分的vtable
8+-------------------+
9| Base2 成员 |
10+-------------------+
11| Derived 成员 |
12+-------------------+
虚继承会引入更复杂的vtable结构:
xxxxxxxxxx
191class Base {
2public:
3 virtual void func() {}
4};
5
6class A : virtual public Base {
7public:
8 void func() override {}
9};
10
11class B : virtual public Base {
12public:
13 void func() override {}
14};
15
16class C : public A, public B {
17public:
18 void func() override {} // 解决菱形继承的二义性
19};
Important
每个包含虚函数的类都有一个vtable
每个对象都有一个vptr指向其类的vtable
虚函数调用通过vptr和vtable实现动态绑定
派生类的vtable继承并可能覆盖基类的虚函数
虚函数调用有一定的性能开销
现代编译器会尝试优化虚函数调用
条件1:基类中必须有虚函数
xxxxxxxxxx
71class Base {
2public:
3 virtual void show() { // 必须是虚函数
4 cout << "Base::show()" << endl;
5 }
6 virtual ~Base() {} // 虚析构函数也很重要
7};
条件2:派生类必须重写(override)基类的虚函数
xxxxxxxxxx
61class Derived : public Base {
2public:
3 void show() override { // 重写基类的虚函数
4 cout << "Derived::show()" << endl;
5 }
6};
条件3:必须通过基类指针或引用调用虚函数
xxxxxxxxxx
121// 正确:通过基类指针调用,会触发动态多态
2Base* ptr = new Derived();
3ptr->show(); // 输出:Derived::show()
4
5// 正确:通过基类引用调用,会触发动态多态
6Derived obj;
7Base& ref = obj;
8ref.show(); // 输出:Derived::show()
9
10// 错误:直接通过对象调用,不会触发动态多态
11Derived obj;
12obj.show(); // 输出:Derived::show()(但这是静态绑定)
虚函数是动态多态的基础,它告诉编译器:
为这个类创建虚函数表(vtable)
在运行时根据对象的实际类型来决定调用哪个函数
xxxxxxxxxx
321class Base {
2public:
3 // 非虚函数 - 静态绑定
4 void normalFunc() {
5 cout << "Base::normalFunc()" << endl;
6 }
7
8 // 虚函数 - 动态绑定
9 virtual void virtualFunc() {
10 cout << "Base::virtualFunc()" << endl;
11 }
12};
13
14class Derived : public Base {
15public:
16 void normalFunc() { // 隐藏基类函数,不是重写
17 cout << "Derived::normalFunc()" << endl;
18 }
19
20 void virtualFunc() override { // 重写基类虚函数
21 cout << "Derived::virtualFunc()" << endl;
22 }
23};
24
25void testPolymorphism() {
26 Base* ptr = new Derived();
27
28 ptr->normalFunc(); // 输出:Base::normalFunc()(静态绑定)
29 ptr->virtualFunc(); // 输出:Derived::virtualFunc()(动态绑定)
30
31 delete ptr;
32}
xxxxxxxxxx
241class Base {
2public:
3 virtual void func() { cout << "Base" << endl; }
4};
5
6class Derived : public Base { // 必须是public继承
7public:
8 void func() override { cout << "Derived" << endl; }
9};
10
11// private继承不支持多态转换
12class PrivateDerived : private Base {
13public:
14 void func() override { cout << "PrivateDerived" << endl; }
15};
16
17void testInheritance() {
18 Derived d;
19 Base* ptr1 = &d; // 正确:public继承支持向上转换
20 ptr1->func(); // 输出:Derived
21
22 PrivateDerived pd;
23 // Base* ptr2 = &pd; // 错误:private继承不允许向上转换
24}
情况1:没有虚函数
xxxxxxxxxx
191class Base {
2public:
3 void show() { // 非虚函数
4 cout << "Base::show()" << endl;
5 }
6};
7
8class Derived : public Base {
9public:
10 void show() { // 隐藏基类函数,不是重写
11 cout << "Derived::show()" << endl;
12 }
13};
14
15void testNoVirtual() {
16 Base* ptr = new Derived();
17 ptr->show(); // 输出:Base::show()(静态绑定)
18 delete ptr;
19}
情况2:直接通过对象调用
xxxxxxxxxx
41void testDirectCall() {
2 Derived obj;
3 obj.show(); // 直接调用,编译时就确定了调用哪个函数
4}
情况3:在构造函数或析构函数中调用虚函数
xxxxxxxxxx
271class Base {
2public:
3 Base() {
4 virtualFunc(); // 在构造函数中调用虚函数
5 }
6
7 virtual ~Base() {
8 virtualFunc(); // 在析构函数中调用虚函数
9 }
10
11 virtual void virtualFunc() {
12 cout << "Base::virtualFunc()" << endl;
13 }
14};
15
16class Derived : public Base {
17public:
18 void virtualFunc() override {
19 cout << "Derived::virtualFunc()" << endl;
20 }
21};
22
23void testConstructorDestructor() {
24 Derived* ptr = new Derived(); // 构造时输出:Base::virtualFunc()
25 delete ptr; // 析构时输出:Base::virtualFunc()
26 // 在构造和析构过程中,虚函数调用不会表现出多态行为
27}
示例
xxxxxxxxxx
751
2
3
4using namespace std;
5
6// 基类
7class Animal {
8public:
9 virtual void makeSound() {
10 cout << "Animal makes a sound" << endl;
11 }
12
13 virtual void move() {
14 cout << "Animal moves" << endl;
15 }
16
17 virtual ~Animal() {
18 cout << "Animal destructor" << endl;
19 }
20};
21
22// 派生类1
23class Dog : public Animal {
24public:
25 void makeSound() override {
26 cout << "Dog barks: Woof!" << endl;
27 }
28
29 void move() override {
30 cout << "Dog runs" << endl;
31 }
32
33 ~Dog() {
34 cout << "Dog destructor" << endl;
35 }
36};
37
38// 派生类2
39class Cat : public Animal {
40public:
41 void makeSound() override {
42 cout << "Cat meows: Meow!" << endl;
43 }
44
45 void move() override {
46 cout << "Cat walks silently" << endl;
47 }
48
49 ~Cat() {
50 cout << "Cat destructor" << endl;
51 }
52};
53
54// 演示动态多态
55void demonstratePolymorphism() {
56 cout << "=== 动态多态演示 ===" << endl;
57
58 // 创建不同类型的动物
59 vector<unique_ptr<Animal>> animals;
60 animals.push_back(make_unique<Dog>());
61 animals.push_back(make_unique<Cat>());
62 animals.push_back(make_unique<Animal>());
63
64 // 通过基类指针调用虚函数,实现动态多态
65 for (const auto& animal : animals) {
66 animal->makeSound(); // 根据实际对象类型调用相应的函数
67 animal->move();
68 cout << "---" << endl;
69 }
70}
71
72int main() {
73 demonstratePolymorphism();
74 return 0;
75}
输出结果:
xxxxxxxxxx
151=== 动态多态演示 ===
2Dog barks: Woof!
3Dog runs
4---
5Cat meows: Meow!
6Cat walks silently
7---
8Animal makes a sound
9Animal moves
10---
11Cat destructor
12Animal destructor
13Dog destructor
14Animal destructor
15Animal destructor
Important
基类有虚函数:使用virtual
关键字
派生类重写虚函数:函数签名必须完全匹配
通过基类指针或引用调用:不能直接通过对象调用
public继承:确保类型转换的合法性
1.构造函数不能设为虚函数
构造函数的作用是创建对象,完成数据的初始化,而虚函数机制被激活的条件之一就是要先创建对象,有了对象才能表现出动态多态。如果将构造函数设为虚函数,那此时构造未执行完,对象还没创建出来,存在矛盾。
2.静态成员函数不能设为虚函数
虚函数的实际调用:this -> vfptr -> vtable -> virtual function
,但是静态成员函数没有this指针,所以无法访问到vfptr。
vfptr是属于一个特定对象的部分,虚函数机制起作用必然需要通过vfptr去间接调用虚函数。静态成员函数找不到这样特定的对象。
3.Inline函数不能设为虚函数
因为inline函数在编译期间完成替换,而在编译期间无法展现动态多态机制,所以起作用的时机是冲突的。如果同时存在,inline失效。
4.普通函数不能设为虚函数
虚函数要解决的是对象多态的问题,与普通函数无关。
抽象类是面向对象编程中的一个重要概念,它定义了一个不能被实例化的类,通常用作其他类的基类来定义通用接口。
在C++中,抽象类是包含至少一个纯虚函数的类。纯虚函数是在声明时被赋值为0的虚函数。
基本语法
xxxxxxxxxx
181class AbstractClass {
2public:
3 // 纯虚函数 - 使类成为抽象类
4 virtual void pureVirtualFunction() = 0;
5
6 // 普通虚函数
7 virtual void virtualFunction() {
8 cout << "AbstractClass::virtualFunction()" << endl;
9 }
10
11 // 普通成员函数
12 void normalFunction() {
13 cout << "AbstractClass::normalFunction()" << endl;
14 }
15
16 // 虚析构函数
17 virtual ~AbstractClass() {}
18};
特点1:不能被实例化
xxxxxxxxxx
31// 错误:不能创建抽象类的对象
2// AbstractClass obj; // 编译错误
3// AbstractClass* ptr = new AbstractClass(); // 编译错误
特点2:可以有指针和引用
xxxxxxxxxx
31// 正确:可以声明抽象类的指针和引用
2AbstractClass* ptr = nullptr;
3AbstractClass& ref = someConcreteObject;
特点3:派生类必须实现所有纯虚函数
xxxxxxxxxx
71class ConcreteClass : public AbstractClass {
2public:
3 // 必须实现纯虚函数,否则ConcreteClass也会成为抽象类
4 void pureVirtualFunction() override {
5 cout << "ConcreteClass::pureVirtualFunction()" << endl;
6 }
7};
图形类
xxxxxxxxxx
1091
2
3
4using namespace std;
5
6// 抽象基类
7class Shape {
8protected:
9 string name;
10
11public:
12 Shape(const string& n) : name(n) {}
13
14 // 纯虚函数 - 计算面积
15 virtual double calculateArea() = 0;
16
17 // 纯虚函数 - 计算周长
18 virtual double calculatePerimeter() = 0;
19
20 // 普通虚函数 - 显示信息
21 virtual void display() {
22 cout << "Shape: " << name << endl;
23 cout << "Area: " << calculateArea() << endl;
24 cout << "Perimeter: " << calculatePerimeter() << endl;
25 }
26
27 // 普通成员函数
28 string getName() const { return name; }
29
30 // 虚析构函数
31 virtual ~Shape() {
32 cout << "Shape destructor: " << name << endl;
33 }
34};
35
36// 具体派生类 - 圆形
37class Circle : public Shape {
38private:
39 double radius;
40
41public:
42 Circle(double r) : Shape("Circle"), radius(r) {}
43
44 double calculateArea() override {
45 return 3.14159 * radius * radius;
46 }
47
48 double calculatePerimeter() override {
49 return 2 * 3.14159 * radius;
50 }
51
52 void display() override {
53 cout << "=== Circle Information ===" << endl;
54 cout << "Radius: " << radius << endl;
55 Shape::display(); // 调用基类的display
56 }
57
58 ~Circle() {
59 cout << "Circle destructor" << endl;
60 }
61};
62
63// 具体派生类 - 矩形
64class Rectangle : public Shape {
65private:
66 double width, height;
67
68public:
69 Rectangle(double w, double h) : Shape("Rectangle"), width(w), height(h) {}
70
71 double calculateArea() override {
72 return width * height;
73 }
74
75 double calculatePerimeter() override {
76 return 2 * (width + height);
77 }
78
79 void display() override {
80 cout << "=== Rectangle Information ===" << endl;
81 cout << "Width: " << width << ", Height: " << height << endl;
82 Shape::display();
83 }
84
85 ~Rectangle() {
86 cout << "Rectangle destructor" << endl;
87 }
88};
89
90// 演示函数
91void demonstrateAbstractClass() {
92 cout << "=== 抽象类演示 ===" << endl;
93
94 // 创建具体对象
95 vector<unique_ptr<Shape>> shapes;
96 shapes.push_back(make_unique<Circle>(5.0));
97 shapes.push_back(make_unique<Rectangle>(4.0, 6.0));
98
99 // 通过抽象类指针调用虚函数
100 for (const auto& shape : shapes) {
101 shape->display();
102 cout << endl;
103 }
104}
105
106int main() {
107 demonstrateAbstractClass();
108 return 0;
109}
动物类
xxxxxxxxxx
781
2
3using namespace std;
4
5// 抽象基类
6class Animal {
7protected:
8 string species;
9 int age;
10
11public:
12 Animal(const string& s, int a) : species(s), age(a) {}
13
14 // 纯虚函数
15 virtual void makeSound() = 0;
16 virtual void move() = 0;
17
18 // 普通虚函数
19 virtual void eat() {
20 cout << species << " is eating." << endl;
21 }
22
23 // 普通成员函数
24 void showInfo() {
25 cout << "Species: " << species << ", Age: " << age << endl;
26 }
27
28 virtual ~Animal() {}
29};
30
31// 抽象派生类 - 哺乳动物
32class Mammal : public Animal {
33protected:
34 bool hasFur;
35
36public:
37 Mammal(const string& s, int a, bool fur) : Animal(s, a), hasFur(fur) {}
38
39 // 实现部分纯虚函数
40 void move() override {
41 cout << species << " walks on land." << endl;
42 }
43
44 // 仍然是抽象类,因为没有实现makeSound()
45 // virtual void makeSound() = 0; // 仍然是纯虚函数
46
47 virtual void giveBirth() {
48 cout << species << " gives birth to live young." << endl;
49 }
50};
51
52// 具体派生类 - 狗
53class Dog : public Mammal {
54public:
55 Dog(int a) : Mammal("Dog", a, true) {}
56
57 void makeSound() override {
58 cout << "Dog barks: Woof! Woof!" << endl;
59 }
60
61 void eat() override {
62 cout << "Dog eats dog food and bones." << endl;
63 }
64};
65
66// 具体派生类 - 猫
67class Cat : public Mammal {
68public:
69 Cat(int a) : Mammal("Cat", a, true) {}
70
71 void makeSound() override {
72 cout << "Cat meows: Meow! Meow!" << endl;
73 }
74
75 void move() override {
76 cout << "Cat walks silently and can climb." << endl;
77 }
78};
Important
包含至少一个纯虚函数
不能被实例化
可以包含普通成员函数和数据成员
派生类必须实现所有纯虚函数才能被实例化
支持多态机制
与其他概念的区别
特性 | 抽象类 | 普通基类 | 接口(纯抽象类) |
---|---|---|---|
纯虚函数 | 至少一个 | 可有可无 | 全部都是 |
实例化 | 不可以 | 可以 | 不可以 |
普通成员 | 可以有 | 可以有 | 通常没有 |
多重继承 | 支持 | 支持 | 常用于多重继承 |
在多继承中,当基类包含虚函数时,派生类会继承这些虚函数,并可以重写它们。每个包含虚函数的基类都会有自己的虚函数表(vtable)。
xxxxxxxxxx
201class Base1 {
2public:
3 virtual void func1() { cout << "Base1::func1" << endl; }
4 virtual void common() { cout << "Base1::common" << endl; }
5 virtual ~Base1() = default;
6};
7
8class Base2 {
9public:
10 virtual void func2() { cout << "Base2::func2" << endl; }
11 virtual void common() { cout << "Base2::common" << endl; }
12 virtual ~Base2() = default;
13};
14
15class Derived : public Base1, public Base2 {
16public:
17 void func1() override { cout << "Derived::func1" << endl; }
18 void func2() override { cout << "Derived::func2" << endl; }
19 void common() override { cout << "Derived::common" << endl; }
20};
派生类对象包含多个vtable指针(vptr),每个基类子对象都有自己的vptr:
xxxxxxxxxx
121// Derived对象的内存布局(简化)
2class Derived {
3 // Base1子对象
4 Base1::vptr -> Derived的Base1 vtable
5 // Base1的数据成员
6
7 // Base2子对象
8 Base2::vptr -> Derived的Base2 vtable
9 // Base2的数据成员
10
11 // Derived的数据成员
12};
vtable内容
xxxxxxxxxx
131// Derived的Base1 vtable
2[
3 &Derived::func1, // 重写的Base1::func1
4 &Derived::common, // 重写的Base1::common
5 &Derived::~Derived // 析构函数
6]
7
8// Derived的Base2 vtable
9[
10 &thunk_to_Derived::func2, // 需要地址调整的thunk
11 &thunk_to_Derived::common, // 需要地址调整的thunk
12 &thunk_to_Derived::~Derived // 析构函数thunk
13]
Thunk机制
由于多继承中基类子对象的地址偏移,编译器使用thunk来调整this
指针:
xxxxxxxxxx
61// 伪代码:thunk_to_Derived::func2
2void thunk_to_Derived::func2(Base2* this) {
3 // 调整this指针从Base2*到Derived*
4 Derived* derived_this = (Derived*)((char*)this - offset_Base2_in_Derived);
5 derived_this->func2(); // 调用实际的Derived::func2
6}
xxxxxxxxxx
101void test() {
2 Derived d;
3 Base1* p1 = &d;
4 Base2* p2 = &d;
5
6 p1->func1(); // 1. 通过Base1的vptr调用
7 p2->func2(); // 2. 通过Base2的vptr调用(可能需要thunk)
8 p1->common(); // 3. 调用Derived::common
9 p2->common(); // 4. 调用Derived::common(通过thunk)
10}
调用过程:
p1->func1()
:直接通过Base1的vtable调用Derived::func1
p2->func2()
:通过Base2的vtable和thunk调用Derived::func2
同名虚函数common()
在两个vtable中都指向Derived::common
xxxxxxxxxx
201class Base {
2public:
3 virtual void func() { cout << "Base::func" << endl; }
4 virtual ~Base() = default;
5};
6
7class Left : virtual public Base {
8public:
9 void func() override { cout << "Left::func" << endl; }
10};
11
12class Right : virtual public Base {
13public:
14 void func() override { cout << "Right::func" << endl; }
15};
16
17class Derived : public Left, public Right {
18public:
19 void func() override { cout << "Derived::func" << endl; }
20};
在虚继承中:
只有一个Base
子对象
Derived::func
解决了菱形继承中的二义性
vtable结构更复杂,包含虚基类表(vbtable)
xxxxxxxxxx
451// 多媒体框架示例
2class Drawable {
3public:
4 virtual void draw() = 0;
5 virtual ~Drawable() = default;
6};
7
8class Clickable {
9public:
10 virtual void onClick() = 0;
11 virtual ~Clickable() = default;
12};
13
14class Button : public Drawable, public Clickable {
15private:
16 string text;
17
18public:
19 Button(const string& t) : text(t) {}
20
21 void draw() override {
22 cout << "Drawing button: " << text << endl;
23 }
24
25 void onClick() override {
26 cout << "Button clicked: " << text << endl;
27 }
28};
29
30// 使用示例
31void handleUI(vector<Drawable*>& drawables, vector<Clickable*>& clickables) {
32 Button btn("OK");
33
34 drawables.push_back(&btn); // 隐式转换为Drawable*
35 clickables.push_back(&btn); // 隐式转换为Clickable*
36
37 // 多态调用
38 for(auto* d : drawables) {
39 d->draw(); // 调用Button::draw
40 }
41
42 for(auto* c : clickables) {
43 c->onClick(); // 调用Button::onClick(可能通过thunk)
44 }
45}
虚拟继承(Virtual Inheritance)是C++中用来解决多重继承中菱形继承问题(Diamond Problem)的重要机制。它确保在复杂的继承层次结构中,共同的基类只有一个实例。
xxxxxxxxxx
521class Base {
2public:
3 int value;
4 Base(int v) : value(v) {
5 cout << "Base constructor: " << value << endl;
6 }
7 virtual void show() {
8 cout << "Base::show() - value: " << value << endl;
9 }
10};
11
12class Left : public Base {
13public:
14 Left(int v) : Base(v) {
15 cout << "Left constructor" << endl;
16 }
17 void leftFunc() {
18 cout << "Left function" << endl;
19 }
20};
21
22class Right : public Base {
23public:
24 Right(int v) : Base(v) {
25 cout << "Right constructor" << endl;
26 }
27 void rightFunc() {
28 cout << "Right function" << endl;
29 }
30};
31
32// 菱形继承 - 问题版本
33class Derived : public Left, public Right {
34public:
35 Derived(int v) : Left(v), Right(v) {
36 cout << "Derived constructor" << endl;
37 }
38};
39
40void problemDemo() {
41 Derived d(42);
42
43 // 编译错误:二义性
44 // d.value; // 错误:不知道是Left::Base::value还是Right::Base::value
45 // d.show(); // 错误:不知道调用哪个Base::show()
46
47 // 必须明确指定
48 d.Left::value = 10;
49 d.Right::value = 20;
50 d.Left::show();
51 d.Right::show();
52}
在上述代码中,Derived
类继承了两个Base
实例:
一个通过Left
继承
一个通过Right
继承
这导致:
内存浪费:Base
的数据被重复存储
二义性:访问Base
成员时不知道访问哪一个
逻辑错误:违反了"一个对象一个基类实例"的原则
xxxxxxxxxx
571class Base {
2public:
3 int value;
4 Base(int v = 0) : value(v) {
5 cout << "Base constructor: " << value << endl;
6 }
7 virtual void show() {
8 cout << "Base::show() - value: " << value << endl;
9 }
10 virtual ~Base() {
11 cout << "Base destructor" << endl;
12 }
13};
14
15// 使用virtual关键字进行虚拟继承
16class Left : virtual public Base {
17public:
18 Left(int v) : Base(v) {
19 cout << "Left constructor" << endl;
20 }
21 void leftFunc() {
22 cout << "Left function - value: " << value << endl;
23 }
24};
25
26class Right : virtual public Base {
27public:
28 Right(int v) : Base(v) {
29 cout << "Right constructor" << endl;
30 }
31 void rightFunc() {
32 cout << "Right function - value: " << value << endl;
33 }
34};
35
36class Derived : public Left, public Right {
37public:
38 // 注意:必须直接初始化虚基类
39 Derived(int v) : Base(v), Left(v), Right(v) {
40 cout << "Derived constructor" << endl;
41 }
42};
43
44void solutionDemo() {
45 cout << "=== Virtual Inheritance Demo ===" << endl;
46 Derived d(42);
47
48 // 现在可以直接访问,没有二义性
49 cout << "d.value = " << d.value << endl;
50 d.show();
51 d.leftFunc();
52 d.rightFunc();
53
54 // 只有一个Base实例
55 cout << "Address of d: " << &d << endl;
56 cout << "Address of Base in d: " << static_cast<Base*>(&d) << endl;
57}
普通多重继承的内存布局
xxxxxxxxxx
111// 普通多重继承
2class Derived : public Left, public Right {
3 // 内存布局(简化):
4 // +0: Left子对象
5 // +0: Base子对象1 (vptr, value)
6 // +8: Left的成员
7 // +16: Right子对象
8 // +16: Base子对象2 (vptr, value) // 重复!
9 // +24: Right的成员
10 // +32: Derived的成员
11};
虚拟继承的内存布局
xxxxxxxxxx
131// 虚拟继承
2class Derived : public Left, public Right {
3 // 内存布局(简化):
4 // +0: Left子对象
5 // +0: vbptr (虚基类表指针)
6 // +8: Left的成员
7 // +16: Right子对象
8 // +16: vbptr (虚基类表指针)
9 // +24: Right的成员
10 // +32: Derived的成员
11 // +40: Base子对象 (只有一个!)
12 // +40: vptr, value
13};
构造顺序
xxxxxxxxxx
381class Base {
2public:
3 Base(int v = 0) : value(v) {
4 cout << "1. Base constructor: " << value << endl;
5 }
6 int value;
7};
8
9class Left : virtual public Base {
10public:
11 Left(int v) : Base(v) {
12 cout << "2. Left constructor" << endl;
13 }
14};
15
16class Right : virtual public Base {
17public:
18 Right(int v) : Base(v) {
19 cout << "3. Right constructor" << endl;
20 }
21};
22
23class Derived : public Left, public Right {
24public:
25 Derived(int v) : Base(v), Left(v), Right(v) {
26 cout << "4. Derived constructor" << endl;
27 }
28};
29
30void constructionOrder() {
31 cout << "Construction order:" << endl;
32 Derived d(42);
33 // 输出:
34 // 1. Base constructor: 42
35 // 2. Left constructor
36 // 3. Right constructor
37 // 4. Derived constructor
38}
重要规则:
虚基类最先构造:Base
首先被构造
最派生类负责:Derived
直接调用Base
的构造函数
中间类的Base调用被忽略:Left
和Right
中的Base(v)
调用被忽略
析构顺序
xxxxxxxxxx
111void destructionOrder() {
2 cout << "\nDestruction order:" << endl;
3 {
4 Derived d(42);
5 } // d离开作用域
6 // 输出:
7 // 4. Derived destructor
8 // 3. Right destructor
9 // 2. Left destructor
10 // 1. Base destructor
11}
虚拟继承是C++中解决菱形继承问题的重要机制:
优点:
解决菱形继承的二义性问题
确保共同基类只有一个实例
提供统一的基类状态管理
支持复杂的多重继承设计
缺点:
增加内存开销(vbptr和vbtable)
降低访问性能(间接寻址)
增加构造/析构的复杂性
使代码更难理解和维护