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

时间:2016-04-02 08:09:00

标签: c++ c++11

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

我的目标是以最佳实践C ++ 11样式实现抽象基类接口,并具有最佳的运行时性能。特别是,我想提供内联/消除无操作析构函数。我还希望通过选择不生成重复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

1 个答案:

答案 0 :(得分:1)

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

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分钟前

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

解包嵌套异常的参考:

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

  

这不会影响表现吗?

不是一点点。

  

但是为每个函数添加一个函数try块很痛苦

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