如果我在C++中为纯虚函数提供实现,有什么用处

sky*_*oor 8 c++

我知道纯虚函数有一个实现是可以的.但是,为什么会这样呢?这两个概念是否存在冲突?有什么用?任何人都可以提供任何例子吗?

Ste*_*sop 7

Effective C++中,Scott Meyers给出了一个示例,当您通过继承重用代码时,它很有用.他从这开始:

struct Airplane {
    virtual void fly() {
        // fly the plane
    }
    ...
};

struct ModelA : Airplane { ... };
struct ModelB : Airplane { ... };
Run Code Online (Sandbox Code Playgroud)

现在,ModelA和ModelB以相同的方式飞行,并且这被认为是飞行飞机的常用方式,因此代码在基类中.然而,并非所有飞机都以这种方式飞行,我们打算将飞机变为多态,因此它是虚拟的.

现在我们添加必须以不同方式飞行的ModelC,但我们犯了一个错误:

struct ModelC : Airplane { ... (no fly function) };
Run Code Online (Sandbox Code Playgroud)

哎呀.ModelC将崩溃.迈耶斯希望编译器警告我们我们的错误.

因此,他fly使用实现在飞机中制作纯虚拟,然后在ModelA和ModelB中放置:

void fly() { Airplane::fly(); }
Run Code Online (Sandbox Code Playgroud)

现在,除非我们显式地在我们想要的默认行为飞我们的派生类中的状态,我们不明白这一点.因此,编译器也告诉我们,而不仅仅是文档告诉我们所有需要检查我们的新平面模型的东西.

这样做,但我觉得它有点弱.理想情况下,我们有一个BoringlyFlyable mixin包含fly的默认实现,并以这种方式重用代码,而不是将代码放在一个基类中,该基类承担飞机不是飞机要求的某些事情.但是如果fly函数实际上做了任何重要的事情,那就需要CRTP :

#include <iostream>

struct Wings {
    void flap() { std::cout << "flapping\n"; }
};

struct Airplane {
    Wings wings;
    virtual void fly() = 0;
};

template <typename T>
struct BoringlyFlyable {
    void fly() {
        // planes fly by flapping their wings, right? Same as birds?
        // (This code may need tweaking after consulting the domain expert)
        static_cast<T*>(this)->wings.flap();
    }
};

struct PlaneA : Airplane, BoringlyFlyable<PlaneA> {
    void fly() { BoringlyFlyable<PlaneA>::fly(); }
};

int main() {
    PlaneA p;
    p.fly();
}
Run Code Online (Sandbox Code Playgroud)

当PlaneA声明从BoringlyFlyable继承时,它通过接口断言它以默认方式传播它是有效的.请注意,BoringlyFlyable可以定义自己的纯虚函数:也许getWings是一个很好的抽象.但由于它是一个模板,它没有必要.

我有一种感觉,这种模式可以替代所有你提供纯虚函数和实现的情况 - 实现可以改为mixin,如果他们想要它可以继承类.但是我无法立即证明这一点(例如,如果Airplane::fly使用私有成员那么它需要相当多的重新设计才能这样做),并且可以说CRTP对于初学者来说有点高性能.它还有更多的代码实际上没有增加功能或类型安全性,它只是明确了Meyer设计中隐含的内容,有些东西可以通过拍打它们的翅膀来飞行,而其他东西则需要做其他的东西.所以我的版本绝不是一个完全的嘘声.


Ale*_*ing 6

GotW#31中得到了解决.摘要:

您可能会有三个主要原因.#1很常见,#2非常罕见,而#3是与弱编译器一起工作的高级程序员偶尔使用的解决方法.

大多数程序员应该只使用#1.

...这适用于纯虚拟析构函数.