我知道,可以将基类中的纯虚函数实现为默认实现。但是我不太明白下面的代码。
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)不必要?
答案 0 :(得分:2)
这是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
这里实际发生的是,仅将A
的{{1}}部分复制到b
中,而a
部分被删除,因此B
实际上是一个真实的,非派生的a
对象。这称为“对象切片”,这是无法在e中使用基础对象的原因。 G。 A
来存储多态对象,但需要指针或引用。
返回虚拟函数:如果您对技术细节感兴趣,可以通过虚拟函数表(简短的vtable)来解决。请注意,这只是事实上的标准,C ++不需要通过vtables实现(实际上,其他支持多态性/继承性的语言,例如Java或Python,也可以实现vtables。)
对于类中的每个虚函数,其对应的vtable中都有一个条目。
直接调用普通函数(即执行到函数地址的无条件分支)。相比之下,对于虚拟函数调用,我们首先需要在vtable中查找地址,然后才能跳转到存储在其中的地址。
现在,派生的类复制其基类的vtable(因此最初它们包含的地址与基类表的地址相同),但是只要您覆盖了函数,就替换相应的地址。
顺便说一句:您可以告诉编译器不要使用vtable,而是显式调用特定的变体:
std::vector
答案 1 :(得分:-2)
首先是
void A1::f1();
void A1::f2()
因为否则这些将是自由函数,并且没有A的方法,
然后必须像使用b.f1()
完成此操作后,您可能知道无法创建纯虚拟类,但是纯虚拟方法仍然可以像在A::f1()
和A::f2()
中看到的那样实现。要创建继承类,必须重写所有纯虚方法。 B使用B::f1()
和B::f2()
来做到这一点,因此您可以毫无问题地创建您的类,并且即使使用纯虚方法A::f1()
仍然可以使用Mother方法的实现。