这似乎是一个基本问题,但我没有看到它问:
假设以下简单案例:
没有虚拟会员。
虚拟继承用于允许多个路径到同一个基地。
根据访问派生类最多的成员所需的时间,虚拟继承的价格是多少?特别是,如果存在非零价格,它是否仅适用于通过多个路径继承的成员或其他成员?
答案 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)
通过额外的间接访问虚拟基类的数据成员。