class base {
public:
virtual void foo()
{
cout << "Base class virutal function " << endl;
}
}
class Derived : public base {
public:
void foo()
{
cout << "Derived class virtual function " << endl;
int main()
{
Base b, *ptr;
Derived d;
ptr = &b;
ptr->foo();
ptr = &d;
ptr->foo();
}
您好, 我对这里的dymnamic绑定有疑问。因为编译器知道当b.foo()存在时它可以使用基本虚拟fumction。当d.foo()存在时,它可以使用foo的派生版本。我的意思是编译器在编译期间有一些信息,但仍然有文献说在运行时决定使用哪个函数。
答案 0 :(得分:5)
在特定的案例 1 中,编译器(至少在理论上)可以确定在每种情况下调用哪个函数,并生成代码以简单地调用这两个函数直接(当我测试它时,大多数编译器将完全按照这种方式优化代码)。
更典型的多态性案例涉及某种用户输入,因此无法静态确定要调用的函数。例如,使用您的Base
和Derived
类,请考虑以下内容:
int main(int argc, char **argv) {
Base b;
Derived d;
Base *arr[] = {&b, &d};
int i = atoi(argv[1]) != 0;
arr[i]->foo();
return 0;
}
在这种情况下,编译器无法确定要静态调用的正确函数 - 根据您在运行时在命令行上传递的内容,它可能使用Base或者foo()
的派生版本。
您似乎也有一种中间案例,您开始尝试将其包含在代码中,但从未真正完成 - 您初始化ptr
以指向您的Base对象,然后指向您的Derived对象,但你永远不会通过ptr
调用函数,只能直接在对象本身上调用。如果您通过指针调用了函数,那么优化比仅使用具体对象直接进行优化更难。在这种情况下,较少的编译器将静态地确定类型,但至少有一些仍然可以/将。
1 嗯,几乎这个具体案例无论如何。就目前而言,代码将无法编译,因为您已经定义了一个名为base
的类(小写'b')并尝试实例化一个名为Base
的类(大写) 'B')。此外,您的“派生”类实际上并非来自Base
,正如您可能想要的那样。一旦修复了......
答案 1 :(得分:5)
在你给出的具体例子中,你是对的;编译器有足够的信息来知道在编译时调用哪个函数。因此编译器不必在运行时查找该函数,但这是一个不影响程序结果的编译器优化 - 编译器可以自由地绕过查找。
答案 2 :(得分:2)
您是说编译器可以注意您将d
转换为Base *ptr
并在运行时解析实际的函数地址吗?
如果是,样本在哪里:
if (rand() > 0.5)
{
ptr = &b;
}
else
{
ptr = &d;
}
ptr->foo();
编译器如何在运行时知道地址?这就是动态绑定的原因。