Polymorphism

谈及多态主要是在考虑具有继承关系的多个类型之间的关系。考虑多态的核心在于引用或指针的 静态类型动态类型 是可能不同的

静态多态 和 动态多态 的一个明显的区别是 是 编译时 还是 运行时 解析调用

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;
}

该如何理解虚函数所带来的这种机制?

有一种不一定准确但简单的理解方式,在使用虚函数时,实际存在一张虚函数表,我们可以理解这张表格记录了派生类类型和成员变量/成员方法入口之间的映射关系,在得知变量的实际类型(派生类类型)时,就可以依据这张表格找到派生类的成员变量或成员方法了,而如果没有这张表格,那么即使能够确定当前的变量的实际类型是某个派生类,但是并不能确定它的变量或方法的入口地址,所以只能够访问静态类型的变量或方法了。

两种多态方式辨析

静态多态是通过编译期间的类型信息来决定调用哪个函数,通常是通过模板或函数重载来实现。在静态多态中,函数的重载解析发生在编译时期,因此调用的函数在编译时期就已经确定。

动态多态是通过运行时的类型信息来决定调用哪个函数,通常是通过虚函数来实现。在动态多态中,函数的解析发生在运行时期,通过基类指针或引用调用虚函数时,实际调用的函数在运行时期根据对象的类型动态确定。

0%