面经:静态多态和动态多态的区别?

文章介绍了C++中的两种多态形式:静态多态(编译时多态)和动态多态(运行时多态)。静态多态通过函数重载和模板实现,效率高,编译时确定函数版本;动态多态依赖于虚函数和继承,提供运行时灵活性。文中给出了实例代码以展示这两种多态性在实际编程中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

静态多态(Static Polymorphism)和动态多态(Dynamic Polymorphism)是C++中两种不同的多态性形式。

1. 静态多态(编译时多态,也称为函数重载或模板多态):
   - 静态多态是通过函数重载或模板特化来实现的。
   - 在编译时确定调用哪个函数或模板的版本。
   - 编译器根据函数或模板的参数类型进行静态绑定(静态解析),决定使用哪个函数或模板。
   - 静态多态性在编译时确定,因此效率较高。
   - 示例:函数重载、模板函数。

静态多态示例代码如下:

#include <iostream>

class Calculator {
public:
    int add(int a, int b) {
        return a + b;
    }

    double add(double a, double b) {
        return a + b;
    }
};

int main() {
    Calculator calculator;
    
    int sum1 = calculator.add(5, 10);
    double sum2 = calculator.add(2.5, 3.7);

    std::cout << "Sum1: " << sum1 << std::endl;  // 输出:Sum1: 15
    std::cout << "Sum2: " << sum2 << std::endl;  // 输出:Sum2: 6.2

    return 0;
}

在上述代码中,Calculator类有两个名为add()的函数,分别接受两个整数和两个浮点数作为参数。通过函数重载,根据传递的参数类型,编译器在编译时会自动选择调用相应的函数。在main()函数中,我们分别传递整数和浮点数给add()函数,并输出结果。

2. 动态多态(运行时多态,也称为虚函数多态):
   - 动态多态是通过基类和派生类之间的继承关系以及虚函数的使用来实现的。
   - 在运行时确定调用哪个函数的版本。
   - 通过基类指针或引用调用虚函数,根据指针或引用指向的实际对象的类型,决定调用哪个派生类的方法。
   - 动态多态性在运行时确定,因此具有更大的灵活性。
   - 示例:虚函数、纯虚函数、虚函数重写(覆盖)。

#include <iostream>

class Shape {
public:
    virtual void draw() {
        std::cout << "Drawing a shape." << std::endl;
    }
};

class Rectangle : public Shape {
public:
    void draw() override {
        std::cout << "Drawing a rectangle." << std::endl;
    }
};

class Circle : public Shape {
public:
    void draw() override {
        std::cout << "Drawing a circle." << std::endl;
    }
};

int main() {
    Shape* shapePtr;

    Rectangle rectangle;
    Circle circle;

    shapePtr = &rectangle;
    shapePtr->draw();  // 输出:Drawing a rectangle.

    shapePtr = &circle;
    shapePtr->draw();  // 输出:Drawing a circle.

    return 0;
}

在上述代码中,Shape类有一个虚函数draw(),而RectangleCircle类都是Shape类的派生类,并重写了draw()函数。在main()函数中,我们创建了一个指向Shape的指针shapePtr,并将其分别指向RectangleCircle对象。通过调用draw()方法,根据指针所指向的实际对象类型,会动态地选择调用相应的draw()函数。输出结果会根据实际对象类型的不同而不同。

注意:协变(Covariance)是重写(Override)的一种特殊情况。当派生类中的函数重写(覆盖)基类的虚函数时,可以通过协变改变返回类型,使其更具体或更窄。

协变是重写的一种特殊情况,它允许派生类的虚函数返回类型与基类的虚函数返回类型不完全相同,但是保持了兼容性和层次关系。通常,在进行协变时,返回类型应该是基类返回类型的派生类型,也就是更具体或更窄的类型。

通过在派生类的函数重写声明中使用相同的函数名称、参数列表,并将返回类型作为基类返回类型的派生类型,可以实现协变。

#include <iostream>

class Animal {
public:
    virtual Animal* create() {
        std::cout << "Creating an animal." << std::endl;
        return new Animal();
    }
    virtual void sound() {
        std::cout << "Animal makes a sound." << std::endl;
    }
};

class Dog : public Animal {
public:
    Dog* create() override {
        std::cout << "Creating a dog." << std::endl;
        return new Dog();
    }
    void sound() override {
        std::cout << "Dog barks." << std::endl;
    }
};

int main() {
    Animal* animalPtr = new Dog();
    Animal* newAnimalPtr = animalPtr->create();

    newAnimalPtr->sound();  // 输出:Dog barks.

    delete animalPtr;
    delete newAnimalPtr;

    return 0;
}

在上述代码中,Animal类有一个虚函数create(),它返回Animal*类型的指针。派生类Dog重写了create()函数,并返回Dog*类型的指针。这里就展现了协变的特性,派生类的返回类型比基类更具体。

main()函数中,我们创建了一个Dog对象,并将其指针赋值给Animal*类型的指针animalPtr。然后,我们调用animalPtrcreate()函数,得到返回的Animal*类型的指针,并将其赋值给newAnimalPtr。虽然实际上create()函数在派生类中被调用,但由于协变的特性,返回的指针类型是Dog*。接下来,我们通过newAnimalPtr调用sound()函数,输出结果表明返回的指针类型确实是Dog*

需要注意的是,协变仅适用于指针或引用类型的返回值,而不适用于返回对象值的情况

简要总结区别
- 静态多态在编译时确定调用的函数版本,而动态多态在运行时确定调用的函数版本。
- 静态多态使用函数重载或模板特化,而动态多态使用虚函数和继承关系。
- 静态多态性具有较高的效率,因为在编译时就确定了调用的函数版本。而动态多态性提供了更大的灵活性,可以根据实际对象的类型在运行时选择调用的函数版本。
- 静态多态是通过静态绑定实现的,而动态多态是通过动态绑定(动态解析)实现的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值