虚拟继承的价格是多少?

时间:2015-12-13 20:41:37

标签: c++ inheritance virtual-inheritance

这似乎是一个基本问题,但我没有看到它问:

假设以下简单案例:

  • 没有虚拟会员。

  • 虚拟继承用于允许多个路径到同一个基地。

根据访问派生类最多的成员所需的时间,虚拟继承的价格是多少?特别是,如果存在非零价格,它是否仅适用于通过多个路径继承的成员或其他成员?

2 个答案:

答案 0 :(得分:5)

  

根据访问派生类最多的成员所需的时间,虚拟继承的价格是多少?

一次偏移查找和一次添加(2条指令和一次内存提取)

  

特别是,如果存在非零价格,它是否仅适用于通过多个路径继承的成员或其他成员?

是的,即便如此也并非总是如此。如果编译器有足够的信息来证明访问不需要通过间接访问,则可以在编译时自由地使查找短路。

  

在这种情况下确切地澄清可能是件好事。 - Nicol Bolas

先生说。

这是一个证明这一点的例子。使用-O2和-S选项进行编译,以查看实施中的优化。

#include <memory>
#include <string>

enum class proof {
    base,
    derived
};

// volatile forces the compiler to actually perform reads and writes to _proof
// Without this, if the compiler can prove that there is no side-effect  of not performing the write,
// it can eliminate whole chunks of our test program!

volatile proof _proof;

struct base
{
    virtual void foo() const {
        _proof = proof::base;
    }

    virtual ~base() = default;
};

struct derived : base
{
    void foo() const override {
        _proof = proof::derived;
    }
};

// factory function
std::unique_ptr<base> make_base(const std::string&name)
{
    static const std::string _derived = "derived";

    // only create a derived if the specified string contains
    // "derived" - on my compiler this is enough to defeat the
    // optimiser

    if (name == _derived) {
        return std::make_unique<derived>();
    }
    else {
        return {};
    }
}

auto main() -> int
{
    // here the compiler is fully aware that p is pointing at a derived
    auto p = std::make_unique<derived>();

    // therefore the call to foo() is made directly (in fact, clang even inlines it)
    p->foo();

    // even here, the compiler 'knows' that b is pointing at a 'derived'
    // so the call to foo is made directly (and indeed on my compiler, completely
    // inlined)
    auto b = std::unique_ptr<base>(new derived);
    b->foo();

    // here we assign a derived to b via indirect construction through a string.
    // Unless the compiler is going to track this string and follow the logic in make_base
    // (and on my compiler it does not) this will prevent the virtual call to foo() from
    // being turned into a direct call.
    // Therefore, this call will be made via the virtual function table of *b
    b = make_base("derived");
    if (b) {
        b->foo();
    }

    return 0;
}

答案 1 :(得分:3)

通过额外的间接访问虚拟基类的数据成员。