带有C ++实现的纯虚函数

时间:2019-03-02 14:23:28

标签: c++ c++11

我知道,可以将基类中的纯虚函数实现为默认实现。但是我不太明白下面的代码。

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)不必要?

2 个答案:

答案 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方法的实现。