在C++中实现类似接口的纯抽象基类的最佳实践

Best practice for implementing an interface-like pure abstract base class in C++?

本文关键字:抽象 基类 最佳 接口 C++ 实现      更新时间:2023-10-16

我想用虚拟析构函数声明一个纯抽象基类。我知道有三种方法可以做到这一点,但我不知道哪种最好,也不知道为什么。

我的目标是以最佳实践C++11风格实现抽象基类接口,并具有最佳运行时性能。特别是,我想提供无操作析构函数的内联/消除。我还想消除与重复的vtables相关的警告,方法是选择一个不生成重复vtables的实现,或者做出明智的决定来抑制警告。

以下是我所知道的实现抽象基类的三种方法:

选项#1

/// A.h:
class A {
public:
    virtual ~A() {}
    virtual int f() = 0;
};

选项#2

/// A.h:
class A {
public:
    virtual ~A();
    virtual int f() = 0;
};
/// A.cpp:
A::~A() {}

选项#3

/// A.h:
class A {
public:
    virtual ~A() = default;
    virtual int f() = 0;
};

这些是我唯一的选择吗?

#1、#2和#3中的哪一个被认为是最佳实践?如果存在权衡(例如运行时性能与编译时性能),请描述它们。

有了选项#1,内联析构函数会内联吗?

我知道选项#1将在每个翻译单元中放入vtable。选项#1在clang中生成-Wweak-vtables警告,并被gcc[1]中的"模糊链接"类别覆盖。选项#3不会生成clang警告——这是否意味着选项#3不会产生vtable?

选项#3与其他选项有何不同?

其他问题也讨论了关于clang警告的类似问题,但我找不到一个问题来具体说明哪种选择被认为是最佳实践以及为什么。

[1]https://gcc.gnu.org/onlinedocs/gcc/Vague-Linkage.html

最佳实践(至少在我负责的时候):

struct A {
    //
    // keep move semantics available - rule of 0, 3, or 5
    // in this case, 5 because we defined a destructor.
    //
    A(const A&) = default;
    A(A&&) = default;
    A& operator=(const A&) = default;
    A& operator=(A&&) = default;
    virtual ~A() = default;
    // non-polymorphic interface in terms of private polymorphic
    // implementation
    int f() 
    try
    {
        // possibly lock a mutex here?
        // possibly some setup code, logging, bookkeeping?
        return f_impl();
    }
    catch(const std::exception& e) {
        // log the nested exception hierarchy
        std::throw_with_nested(std::runtime_error(__func__));
    }   
private:
    virtual int f_impl() = 0;
};

为什么在你看来,为f()设置一个try-catch块很重要einpoklum 16分钟前

@我很高兴你问我。因为如果你在每个方法和每个函数中都这样做,并抛出一个包含函数名(和任何相关参数)的嵌套异常,这意味着当你最终捕捉到异常时,你可以将所有嵌套异常打开到你的日志文件或cerr中,你就会得到一个完美的堆栈跟踪,准确地指向问题。

展开嵌套异常的参考:

http://en.cppreference.com/w/cpp/error/throw_with_nested

这不会影响表演吗?

一点也不。

但在每个功能中添加一个功能尝试块是很痛苦的

要想重现一个你不知道它是如何或为什么发生的问题,也不知道它的背景,这是一个更大的痛苦。相信我。。。