谈及多态主要是在考虑具有继承关系的多个类型之间的关系。考虑多态的核心在于引用或指针的 静态类型 与 动态类型 是可能不同的
静态多态 和 动态多态 的一个明显的区别是 是 编译时 还是 运行时 解析调用
static polymorphism
CRTP 是 Curiously Recurring Template Pattern 的缩写,中文翻译为“奇特的递归模板模式”。它是一种 C++ 编程技术,通常用于实现静态多态性,即在编译期间确定调用的函数。
CRTP 的基本思想是,基类模板派生出一个子类,并将子类作为模板参数传递给基类模板。子类可以通过继承基类模板,并传递自身类型作为模板参数,从而在编译期间实现基类和子类之间的静态多态性。
下面是一个简单的示例来说明 CRTP 的使用方式:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
| template <typename Derived>
class Base {
public:
void interface() {
// 调用派生类的实现
static_cast<Derived*>(this)->implementation();
}
// 基类提供的默认实现
void implementation() {
// 默认实现
}
};
class Derived1 : public Base<Derived1> {
public:
void implementation() {
// 派生类自定义的实现
}
};
class Derived2 : public Base<Derived2> {
// Derived2没有实现 implementation 函数,将使用基类提供的默认实现
};
int main() {
Derived1 d1;
Derived2 d2;
d1.interface(); // 调用 Derived1 的实现
d2.interface(); // 调用 Derived2 的默认实现
return 0;
}
|
dynamic polymorphism
动态多态性是面向对象编程中的一个重要概念,通常通过虚函数和基类指针(或引用)实现。
使用指针时的示例: ptr
的静态类型是基类 Base
,其实际类型是派生类 Derived
。如果基类中的 display()
方法没有声明为虚函数,那么在通过 ptr
调用 display()
时,是无法找到派生类的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
| #include <iostream>
// 基类
class Base {
public:
// 虚函数
virtual void display() {
std::cout << "Base::display()" << std::endl;
}
};
// 派生类
class Derived : public Base {
public:
// 覆盖基类虚函数
void display() const override {
std::cout << "Derived::display()" << std::endl;
}
};
int main() {
// 创建基类指针,并指向派生类对象
Base* ptr = new Derived();
// 调用虚函数,实现动态多态
ptr->display(); // 输出 Derived::display()
// 释放内存
delete ptr;
return 0;
}
|
使用引用的示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
| #include <iostream>
class Base {
public:
virtual void Display() const {
std::cout << "Base Display\n";
}
};
class Derived : public Base {
public:
void Display() const override {
std::cout << "Derived Display\n";
}
};
void Print(Base& obj) {
obj.Display(); // 实际调用取决于 obj 的动态类型
}
int main() {
Base b;
Derived d;
Print(b); // 输出:Base Display
Print(d); // 输出:Derived Display
return 0;
}
|
该如何理解虚函数所带来的这种机制?
有一种不一定准确但简单的理解方式,在使用虚函数时,实际存在一张虚函数表,我们可以理解这张表格记录了派生类类型和成员变量/成员方法入口之间的映射关系,在得知变量的实际类型(派生类类型)时,就可以依据这张表格找到派生类的成员变量或成员方法了,而如果没有这张表格,那么即使能够确定当前的变量的实际类型是某个派生类,但是并不能确定它的变量或方法的入口地址,所以只能够访问静态类型的变量或方法了。
两种多态方式辨析
静态多态是通过编译期间的类型信息来决定调用哪个函数,通常是通过模板或函数重载来实现。在静态多态中,函数的重载解析发生在编译时期,因此调用的函数在编译时期就已经确定。
动态多态是通过运行时的类型信息来决定调用哪个函数,通常是通过虚函数来实现。在动态多态中,函数的解析发生在运行时期,通过基类指针或引用调用虚函数时,实际调用的函数在运行时期根据对象的类型动态确定。