C++多态与虚函数

2024-02-20 13:12:59 浏览数 (1)

1. 概念

1.1 多态

不同类的对象对相同的方法或函数产生有不同的反应。多态的实现依赖于虚函数

静态多态和动态多态

静态多态(编译时多态)

  1. 这是通过方法重载实现的一种多态性形式。
  2. 编译时,编译器确定使用哪个方法。重载方法的选择发生在编译阶段,因此它是静态的,编译器会确定调用哪个方法。

动态多态(运行时多态)

  1. 这是通过方法重写和继承实现的一种多态性形式。
  2. 运行时,程序确定使用哪个方法。

1.2 虚函数

虚函数是指使用了修饰符virtua修饰过后的函数,而且定义虚函数的函数必须为类的成员函数,虚函数被继承后所继承的派生类都是为虚函数,析构函数可以定义为虚函数,但是构造函数(与友员函数)却不能定义为虚函数。


2. 虚函数

2.1 作用

虚函数的作用主要是实现了多态的机制。基类定义虚函数,子类可以重写该函数;在派生类中对积累定义的虚函数进行重写时,需要在派生类中声明该方法为虚方法。

2.2 析构函数可以为虚函数吗

当使用多态特性,让基类指针指向派生类对象时,如果析构函数不是虚函数,通过基类指针销毁派生类对象时,会调用静态绑定的析构函数,也就是基类的析构函数,从而只能销毁属于基类的元素,导致派生类析构不完全,程序就会出现资源泄露或未定义行为。

2.3 构造函数可以为虚函数吗

在C 中,构造函数(包括拷贝构造函数和移动构造函数)不能声明为虚函数。虚函数在运行时通过对象的虚函数表(vtable)来调用,而构造函数在对象被创建之前执行,因此在对象存在之前虚函数表也不存在,无法实现虚函数的多态性。

2.4 纯虚函数

纯虚函数必须在基类中定义,没有具体的实现代码,只有函数声明。它规定派生类必须提供该函数的具体实现。 注意:纯虚函数的类无法被实例化,只能作为基类来派生其他类。派生类必须提供对应的纯虚函数的具体实现。

示例(定义纯虚函数的语法是在函数声明后面加上 = 0):

代码语言:javascript复制
class AbstractBase {
public:
    virtual void pureVirtualFunction1() = 0; // 第一个纯虚函数
    virtual void pureVirtualFunction2(int x) = 0; // 第二个纯虚函数带参数
    virtual void pureVirtualFunction3(double y, const std::string& str) = 0; // 第三个纯虚函数带多个参数
};
};

class Derived : public AbstractBase {
public:
    void pureVirtualFunction1() override { //override表明重写虚函数,可不加
        // 提供具体的实现
        // ...
    }

    void pureVirtualFunction2(int x) override {
        // 提供具体的实现
        // ...
    }

    void pureVirtualFunction3(double y, const std::string& str) override {
        // 提供具体的实现
        // ...
    }
};

2.5 虚表原理?虚表指针存放在哪里?

  1. 虚表的工作原理:
    • 每个包含虚函数的C 类都有一个对应的虚函数表。
    • 虚表中存储了该类中的虚函数地址。
    • 每个对象都包含一个指向其类的虚表指针。
    • 当你调用一个虚函数时,实际上是通过对象的虚表指针来查找适当的虚函数地址,然后调用该函数。
  2. 虚表指针存放在哪里:通常存放在对象内部,即对象的地址就是虚表指针的地址。

2.6 虚函数是类的定义出现还是对象的时候出现

虚函数的定义在类的定义中,而不是在创建对象时出现。

2.4 函数重载和重写区别

  1. 范围区别:重写和被重写的函数在不同的类中,重载和被重载的函数在同一类中。
  2. 参数区别:重写的函数名,参数个数,类型,顺序以及返回值类型完全一样,而重载的参数个数,类型,顺序至少有一个不同。
  3. virtual的区别:重写的基类函数必须要有virtual修饰,重载函数和被重载函数可以被virtual修饰,也可以没有

2.5 C 多态示例

代码语言:javascript复制
#include <iostream>
using namespace std;

class Shape {
public:
    virtual double area() const = 0; // 纯虚函数
};

class Circle : public Shape {
private:
    double radius;

public:
    Circle(double r) : radius(r) {}

    double area() const {
        return 3.14159265359 * radius * radius;
    }
};

class Rectangle : public Shape {
private:
    double width;
    double height;

public:
    Rectangle(double w, double h) : width(w), height(h) {}

    double area() const {
        return width * height;
    }
};

int main() {
    Circle circle(5.0);
    Rectangle rectangle(4.0, 6.0);

    Shape* shape1 = &circle;
    Shape* shape2 = &rectangle;

    std::cout << "面积1: " << shape1->area() << std::endl;
    std::cout << "面积2: " << shape2->area() << std::endl;

    return 0;
}

致读者

非知之难,行之为难;非行之难,终之斯难

0 人点赞