考虑以下设置。
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)
也许更好的表达方式是“分配在堆栈上并用作'常规值类型'的对象的行为是否不同?” 如果您知道我的意思但有更好的表达方式,请在这里帮助我使用术语!
我试图理解的一点是,您可以在编译时将基类的定义“展平”到您在堆栈上分配实例的派生类中。