MST

星途 面试题库

面试题:C++中虚拟函数与普通成员函数在多态实现上的区别

请详细阐述C++中虚拟函数和普通成员函数在实现多态时的不同机制,并且通过一段简单代码示例说明。
23.1万 热度难度
编程语言C++

知识考点

AI 面试

面试题答案

一键面试

虚拟函数与普通成员函数实现多态机制的不同

  1. 虚拟函数
    • 机制:C++通过虚函数表(vtable)和虚指针(vptr)来实现多态。当一个类中定义了虚函数,编译器会为该类生成一个虚函数表,其中存放了虚函数的地址。每个包含虚函数的类的对象都有一个虚指针,它指向该类的虚函数表。在运行时,根据对象的实际类型,通过虚指针找到对应的虚函数表,进而调用合适的虚函数版本,实现动态绑定,即多态。
    • 特点:动态绑定,根据对象的实际类型在运行时决定调用哪个函数版本,提高了程序的灵活性。
  2. 普通成员函数
    • 机制:普通成员函数在编译时就确定了调用关系,即静态绑定。编译器根据对象的声明类型来确定调用哪个函数版本。
    • 特点:静态绑定,效率相对较高,但缺乏灵活性,不能根据对象的实际类型来动态选择函数版本。

代码示例

#include <iostream>

class Animal {
public:
    // 虚函数
    virtual void speak() {
        std::cout << "Animal speaks" << std::endl;
    }
};

class Dog : public Animal {
public:
    // 重写虚函数
    void speak() override {
        std::cout << "Dog barks" << std::endl;
    }
};

class Cat : public Animal {
public:
    // 重写虚函数
    void speak() override {
        std::cout << "Cat meows" << std::endl;
    }
};

void makeSound(Animal& animal) {
    // 这里会根据animal的实际类型调用相应的speak函数,体现多态
    animal.speak();
}

class Shape {
public:
    // 普通成员函数
    void draw() {
        std::cout << "Drawing a shape" << std::endl;
    }
};

class Circle : public Shape {
public:
    // 普通成员函数
    void draw() {
        std::cout << "Drawing a circle" << std::endl;
    }
};

class Rectangle : public Shape {
public:
    // 普通成员函数
    void draw() {
        std::cout << "Drawing a rectangle" << std::endl;
    }
};

void drawShape(Shape& shape) {
    // 这里根据shape的声明类型调用draw函数,不会体现多态
    shape.draw();
}

int main() {
    Animal animal;
    Dog dog;
    Cat cat;

    makeSound(animal);
    makeSound(dog);
    makeSound(cat);

    Shape shape;
    Circle circle;
    Rectangle rectangle;

    drawShape(shape);
    drawShape(circle);
    drawShape(rectangle);

    return 0;
}

在上述代码中,Animal类的speak函数是虚函数,DogCat类重写了speak函数。在makeSound函数中,通过Animal&引用调用speak函数,根据传入对象的实际类型(DogCat)来动态调用相应的speak版本,体现了多态。而Shape类的draw函数是普通成员函数,CircleRectangle类也有自己的draw函数,但在drawShape函数中,根据Shape&引用的声明类型,始终调用Shape类的draw函数,不会体现多态。