我知道可以在基类中实现纯虚函数,作为默认实现。但是我不太明白下面的代码。
class A {
public:
virtual void f1() = 0;
virtual void f2() = 0;
};
void A::f1(){
cout << "base f1" << endl;
f2();
}
void A::f2(){
cout << "base f2" << endl;
}
class B: public A {
public:
void f1() { A::f1(); }
void f2() { cout << "derived f2" << endl; }
};
int main(){
B b;
b.f1();
}
为什么 B::f1() 调用 B::f2() 而不是 A::f2。我知道它会这样,但为什么呢? 我错过了什么基础知识。
另一个问题,在基类中实现纯虚函数是否不需要 pure(=0)?
最佳答案
这是 C++ 标准为虚函数定义的行为:调用可用的派生类型最多的版本。
当然,对于普通对象,最派生的类型是对象本身:
B b;
b.f1(); // of course calls B's version
有趣的是,如果您有指针或引用:
B b;
A& ar = b;
A* ap = &b;
// now both times, B's version will be called
ar.f1();
ap->f1();
同样的事情发生在内部 f1,实际上,你隐含地做了:
this->f2(); // 'this' is a POINTER of type A* (or A const* in const functions).
没有出现这种情况时有一种现象(下面的例子需要拷贝构造函数):
B b;
A a = b; // notice: not a pointer or reference!
A.f1(); // now calls A's version
这里实际发生的是只有 b
的 A
部分被复制到 a
和 B
部分被删除,所以 a
实际上是一个真实的、非派生的 A
对象。这称为“对象切片”,这就是您不能在 e 中使用基础对象的原因。 G。一个 std::vector
来存储多态对象,但需要指针或引用。
回到虚函数:如果您对技术细节感兴趣,这可以通过虚函数表(简称 vtables)来解决。请注意,这只是一个事实上的标准,C++ 不需要通过 vtables 实现(实际上,其他支持多态/继承的语言,如 Java 或 Python,也实现了 vtables)。
对于类中的每个虚函数,在其对应的 vtable 中都有一个条目。
直接调用普通函数(即执行到函数地址的无条件分支)。相比之下,对于虚函数调用,我们首先需要在 vtable 中查找地址,然后才能跳转到存储在那里的地址。
派生类现在复制它们基类的 vtable(因此最初它们包含与基类表相同的地址),但是一旦您覆盖函数就替换适当的地址。
顺便说一句:你可以告诉编译器不要使用 vtable,而是显式调用特定的变体:
B b;
A& a = b;
a.A::f1(); // calls A's version inspite of being virtual,
// because you explicitly told so
b.A::f1(); // alike, works even on derived type
关于c++ - 在 C++ 中实现的纯虚函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54959504/