为什么在运行时处理虚拟函数
Why are virtual functions handled at runtime?
编译器当然足够聪明,可以在某些情况下准确地推断出您想要的函数,但为什么其他情况需要运行时支持?
因为我们并不总是知道在运行时将面对什么实例。
例如,您有类:SuperClass
、Subclass1
和Subclass2
,它们都有一个方法doACoolThing()
。用户按下按钮0
、1
或2
,然后根据他的输入,创建适当类的实例,并调用其doACoolThing()
方法。
我们(以及编译器)无法确定在运行时将调用什么类的方法。
这就是为什么这样的技巧需要运行时支持。
一个小例子来说明一个想法(附言:不要这样写代码,这里只是为了说明多态性:):
#include <iostream>
using namespace std;
class SuperClass
{
public:
virtual void doACoolThing();
};
void SuperClass::doACoolThing()
{
cout << "Hello from the SuperClass!" << endl;
}
class Subclass1 : public SuperClass
{
virtual void doACoolThing() override;
};
void Subclass1::doACoolThing()
{
cout << "Hello from the Subclass1" << endl;
}
class Subclass2 : public SuperClass
{
virtual void doACoolThing() override;
};
void Subclass2::doACoolThing()
{
cout << "Hello from the Subclass2" << endl;
}
int main()
{
int userInput;
cout << "Enter 0, 1 or 2: ";
cin >> userInput;
SuperClass *instance = nullptr;
switch (userInput)
{
case 0:
instance = new SuperClass();
break;
case 1:
instance = new Subclass1();
break;
case 2:
instance = new Subclass2();
break;
default:
cout << "Unknown input!";
}
if (instance)
{
instance->doACoolThing();
delete instance;
}
return 0;
}
考虑以下代码:
Derived1 var1 = <something>;
Derived2 var2 = <something>;
int x;
cin >> x;
Base *baseptr = x ? &var1 : &var2;
baseptr->virtfun();
编译器不知道用户将输入什么,因此无法判断baseptr
是指向Derived1
还是指向Derived2
的实例。
假设您依赖用户输入来决定要创建哪个子类。
class Base
{
public:
void f();
}
class Derived1: public Base
{
public:
void f();
}
class Derived2: public Base
{
public:
void f();
}
int choice;
cin >> choice;
Base *pB = NULL;
if (choice == 1)
{
pB = new Derived1;
}
else
{
pB = new Derived2;
}
pB->f();
如果没有虚拟函数,如果您想根据不同的实例选择f
,编译器如何知道在运行时调用f
的正确版本?只是没有办法。
原因包括:
-
只有在运行时才知道一些输入(键盘、鼠标、文件、数据库、网络、硬件设备等),这些输入将决定实际的数据类型,从而决定需要调用的成员函数
-
没有内联的函数以及从具有不同派生对象的许多地方调用的函数将需要进行虚拟调度——静态调度的替代方案相当于每种类型的"实例化"(ala模板,并可能隐含一些代码膨胀)
-
使用虚拟调度的函数可以在链接到可执行文件的对象中编译,并使用指针调用它们在编译时从未知道的参数类型
-
虚拟调度可以提供一种"编译防火墙"(很像指向Implementation或pImpl习语的指针),这样,对定义函数实现的文件的更改就不需要对包含接口的头进行更改,这意味着客户端代码需要重新链接,而不需要重新编译:这可以在企业环境中节省大量时间
-
在程序的各个阶段跟踪类型通常太复杂了:即使可能从代码的编译时分析中知道,编译器也只需要付出有限的努力来识别编译时常数/确定性数据,这并不涉及跟踪任意复杂的操作,比如说,当调用最终被虚拟调度时,可能会使用的指向基的指针的常量。
- 容器和实现状态机的变量尤其如此,它们具有关于何时应删除指向基的指针并重置为不同派生类型的复杂规则
- C++无法定义虚拟函数 OUTER 类和头文件
- 用常见虚拟函数实现的任意组合来实现派生类的正确方法是什么
- 尝试将unique_ptrs推送到向量时使用纯虚拟函数错误
- 有没有比在库中添加一个并非由所有派生类实现的新虚拟函数更好的设计实践
- 类型擦除的std::function与虚拟函数调用的开销
- 重写虚拟函数和继承
- 用纯虚拟函数兜圈子
- 为什么使用存储在虚拟方法表中的地址调用虚拟函数的函数会返回垃圾?
- 禁止子函数调用父级的抽象(或虚拟)函数
- 无法在子类中使用虚拟函数C++
- 无法在派生对象上运行虚拟函数
- 我可以调用从 main() 覆盖的虚拟函数吗?
- 在 C++ 中将函数获取和设置为虚拟函数
- 使用在堆栈上创建的对象调用虚拟函数
- 为什么在这种情况下不调用我的虚拟函数实现?
- 在C++中使虚拟函数私有化
- 模板继承类中的虚拟函数
- 为什么构造函数的虚拟函数调用有时有效,但其他调用却无效
- doxygenc++虚拟函数和实现
- 如何从派生类函数中调用虚拟函数