【发布时间】:2013-05-13 04:35:17
【问题描述】:
考虑以下设置。
class I
{
public:
virtual void F() = 0;
};
class A : public I
{
public:
void F() { /* some implementation */ }
};
class B : public I
{
public:
void F() { /* some implementation */ }
};
这让我可以编写如下函数。
std::shared_ptr<I> make_I(bool x)
{
if (x) return std::make_shared<A>();
else return std::make_shared<B>();
}
在这种情况下,我为继承和多态性付出了一些代价,即拥有一个 vtable,并且在如下使用时不能内联对 F 的调用(如果我错了,请纠正我)。
auto i = make_I(false);
i->F(); //can't be inlined
我想知道的是,当使用A 或B 作为堆栈上分配的对象时,我是否必须支付同样的费用,如下面的代码所示。
A a;
a.F();
A 和 B 在堆栈上分配时是否有 vtables?对F 的调用可以内联吗?
在我看来似乎编译器可以为继承层次结构中的类创建两种内存布局——一种用于堆栈,一种用于堆。这是 C++ 编译器会/可能会做的事情吗?或者是否有理论上或实践上的原因它不能?
编辑:
我看到一条评论(看起来好像被删除了)实际上提出了一个很好的观点。您总是可以执行以下操作,然后在堆栈上分配 A a 可能不是我想要达到的重点......
A a;
A* p = &a;
p->F(); //likely won't be inlined (correct me if I'm wrong)
也许更好的表达方式是“分配在堆栈上的对象的行为是否不同并用作'常规值类型'?”如果您知道我的意思但有更好的表达方式,请在这里帮助我解决术语问题!
我要说明的是,您可以在编译时将基类的定义“扁平化”到派生类中,以便在堆栈上分配一个实例。
【问题讨论】:
-
@DyP 这看起来肯定是相关的——谢谢,我会通读一遍。但我不确定它是否重复。区别在于,在这里我还对编译器是否可以根据使用情况改变类型的内存布局(和大小)等内容感兴趣。
-
@TimothyShields 班级的大小和布局总是一样的。
-
@brianbeuning 是的,你是对的 - 如果这不是真的,
sizeof(A)将如何工作?我认为 Scott Jones 的回答是我真正想要的。
标签: c++ inheritance