这里我在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。
另外我想知道,内联虚拟函数安全吗?
答案 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*
,但我认为这不是一个好主意。改为改变你的数据结构。