面试题答案
一键面试静态多态
静态多态是通过函数重载和模板实现的,在编译期就确定调用哪个函数。
函数重载示例:
#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 calc;
std::cout << "Int addition: " << calc.add(3, 5) << std::endl;
std::cout << "Double addition: " << calc.add(3.5, 5.5) << std::endl;
return 0;
}
模板示例:
#include <iostream>
template <typename T>
T add(T a, T b) {
return a + b;
}
int main() {
std::cout << "Int addition: " << add(3, 5) << std::endl;
std::cout << "Double addition: " << add(3.5, 5.5) << std::endl;
return 0;
}
动态多态
动态多态是通过虚函数和指针或引用来实现的,在运行期才确定调用哪个函数。
示例:
#include <iostream>
class Animal {
public:
virtual void speak() {
std::cout << "I am an animal" << std::endl;
}
};
class Dog : public Animal {
public:
void speak() override {
std::cout << "I am a dog, woof!" << std::endl;
}
};
class Cat : public Animal {
public:
void speak() override {
std::cout << "I am a cat, meow!" << std::endl;
}
};
int main() {
Animal* animal1 = new Dog();
Animal* animal2 = new Cat();
animal1->speak();
animal2->speak();
delete animal1;
delete animal2;
return 0;
}
虚函数表(vtable)和虚函数指针(vptr)的作用
- 虚函数表(vtable):是一个存储类虚函数地址的数组。每个包含虚函数的类都有一个对应的虚函数表。虚函数表为每个虚函数在表中分配一个条目,存储该虚函数的实际地址。如果派生类重写了基类的虚函数,虚函数表中该函数的条目将更新为派生类中重写函数的地址。
- 虚函数指针(vptr):每个包含虚函数的类的对象都有一个虚函数指针。这个指针指向该对象所属类的虚函数表。当通过指针或引用调用虚函数时,程序会首先通过虚函数指针找到虚函数表,然后在虚函数表中找到对应的虚函数地址并调用。
多个虚函数时 vtable 和 vptr 的工作方式
当一个类中有多个虚函数时,虚函数表会为每个虚函数分配一个条目,按照它们在类中声明的顺序排列。每个虚函数的地址被存储在对应的条目中。对象的虚函数指针仍然指向该类的虚函数表。
例如,假设有如下类:
class Base {
public:
virtual void func1() {}
virtual void func2() {}
virtual void func3() {}
};
Base类的虚函数表会有三个条目,分别存储func1、func2、func3的地址。如果有派生类Derived继承自Base并重写了func2:
class Derived : public Base {
public:
void func2() override {}
};
Derived类的虚函数表中func2的条目将被更新为Derived::func2的地址,而func1和func3的条目仍然指向Base类中的对应函数(除非Derived也重写了它们)。当通过Base* 或Derived* 指针调用虚函数时,程序通过虚函数指针找到虚函数表,根据调用的虚函数在表中的索引找到对应的函数地址并调用。