c++ - 在 C++ 中实现类似接口(interface)的纯抽象基类的最佳实践?

标签 c++ c++11

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

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

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

选项#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;

};

Why is it important in your opinion to have a try-catch block for f()? – einpoklum 16 mins ago

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

展开嵌套异常的引用:

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

doesn't that hurt performance?

不是一点点。

But it's a pain to add a function try block to every function

如果您不知道问题是如何发生或为什么发生的,并且对上下文没有任何线索,则必须尝试重现一个问题,这是一种更大的痛苦。相信我...

关于c++ - 在 C++ 中实现类似接口(interface)的纯抽象基类的最佳实践?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36370846/

相关文章:

c++ - 调用错误的运算符(operator)

c++ - 从数字和 C++ 中的位掩码生成数字数组

c++ - Qt5 抛出 std::bad_alloc

C++11 风格的 SFINAE 和模板实例化的函数可见性

c++ - 包含可变参数模板的元组

c++ - 向现有项目添加单元测试

c++ - 函数运行完美,但返回后值发生变化

C++ vector 迭代错误

c++ - 在单个 auto 语句中声明多个变量

c++ - 使用任何参数创建 std::functions 的 unordered_map?