在派生类中声明的虚函数

时间:2012-06-20 06:29:56

标签: c++ virtual-functions inlining

这里我在Derived类中声明了另一个虚函数。

#include <iostream>

using namespace std;

class A{
    string s;
    public:
    A(const string& name) : s(name){}
     virtual void f1(){cout << "f1 of base class" << endl;};
};

class B : public A{
    string p;
    public:
    B(const string& name) : A(name){}
    virtual void f2(){cout << "virtual function of derived class" << endl;}
    void f1(){cout << "f1 of derived class";}
};

int main() {
    A* arr[] = {new A("John"),new B("Bob")};
    arr[0]->f1();
    arr[1]->f1();
    arr[1]->f2();
    return 0;
}

函数调用 arr [1] - &gt; f2()给出错误“ '类A'没有名为'f2'的成员 “。我想知道为什么_vptr指向基类的VTABLE,即使B被上升到A。

另外我想知道,内联虚拟函数安全吗?

5 个答案:

答案 0 :(得分:8)

因为 没有这样的成员。 virtual函数从首先声明它们的类到所有派生类发生。他们不会冒泡。当编译器查看arr[ i ]->f2()时,它会查找arr的类型定义。 arr具有(静态)类型A。因此,编译器查找A的定义以获得f2的合适定义,但找不到任何定义。因此诊断。

virtual函数可以安全地内联。实际上,所有类内定义都是由编译器内联隐式内联的。因此,您的A::f1已经内联。

答案 1 :(得分:3)

您尝试在f2()上致电A*,而A未宣布f2()

virtual表示当通过指针调用基类(包括对象本身的类)时,该方法在运行时基于vptr表解析。你正在调用它的对象。它不会使方法在基类中可见。

内联虚拟函数非常安全,但您不会必然获得您期望的性能提升。内联意味着函数代码在调用时由编译器复制粘贴。 See The C++ Super-FAQ on inlining and why it's a pie in the sky

内联是编译时间概念。在指向基类的指针上调用的虚方法调用在运行时处解析。

即。以下对虚拟函数的调用可以内联:

B myB("The B.");
b.f1(); // not virtual, might be inlined

答案 2 :(得分:2)

简单易行:

  

另外我想知道,内联虚拟函数安全吗?

是的,虚拟函数可以内联,但保证多态性始终有效。

例如:

B b;
b.f2();

可以在编译时解析,而不使用虚拟表,因为编译器知道对象的类型为B

  

我想知道为什么_vptr指向基类的VTABLE,即使arr [1]被上传到B。

没有。它指向类B的虚拟表,但编译器没有看到它。虚拟表是一个实现细节。您在A*上调用了一个函数,因此B的方法不可见。在这个简单的例子中,它很容易,但是一般来说,编译器怎么能告诉arr[1]实际上是指向B的指针?这就是多态性的重点,你抽象出派生类型。

你能做的是:

dynamic_cast<B*>(arr[1]) ? (dynamic_cast<B*>(arr[1]))->f2() : (void)0;

答案 3 :(得分:1)

以这种方式思考。在编译时,编译完全忽略了arr[i]指向的对象的实际类型。该类型仅在运行时已知。所以当你写这样的东西时:

arr[i]->f2();

编译器不知道要使该调用发生的代码。什么是f2?它是在B中声明的f2吗?如果你还有一个名为C的A的子类,它也有一个名为f2的虚函数,并且arr [i]指向的对象恰好是C类的,那该怎么办?我们不知道。

您将立即看到在这种情况下唯一合理的行为是产生错误。

答案 4 :(得分:0)

arr[1]属于A*类型,没有名为f2的方法。您必须将arr[1]投射到B*,但我认为这不是一个好主意。改为改变你的数据结构。