是相对于对象是在堆栈上还是在堆上分配的继承成本
Are inheritance costs relative to whether objects are allocated on the stack or heap?
考虑以下设置:
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>();
}
在这种情况下,我为继承和多态性付出了一些代价,即拥有一个虚函数表,并且当像下面这样使用时,对F
的调用不能内联(如果我错了请纠正我)。
auto i = make_I(false);
i->F(); //can't be inlined
我想知道的是,当我使用A
或B
作为堆栈上分配的对象时,是否必须支付相同的成本,就像下面的代码一样。
A a;
a.F();
在堆栈上分配A
和B
时有变量吗?对F
的调用可以内联吗?
对我来说似乎编译器可以为继承层次结构中的类创建两种内存布局——一种用于堆栈,一种用于堆。这是c++编译器将要/可能做的吗?还是存在理论上或实践上的原因?
编辑:
我看到了一条评论(看起来好像被删除了),它实际上提出了一个很好的观点。你总是可以这样做,然后A a
被分配到堆栈上可能不是我想要得到的突出点…
A a;
A* p = &a;
p->F(); //likely won't be inlined (correct me if I'm wrong)
也许更好的表达方式是"在堆栈上分配并用作'常规值类型'的对象的行为是否不同?"请帮助我这里的术语,如果你知道我的意思,但有一个更好的方式来把它!
我想说的是,你可以在编译时将基类的定义"扁平化"到你在堆栈上分配实例的派生类中。
我认为您的问题确实与编译器是否具有对象的静态知识并且可以省略虚函数表查找(您在编辑中提到了这一点)有关,而不是对象存在的位置是否有区别-堆栈或堆。是的,在这种情况下,许多编译器可以省略虚拟分派。
您的问题的编辑询问您是否可以将基类A
的定义扁平化到派生类B
中。如果编译器在编译时能够判断出一个对象将只包含B
的实例,那么它可以在运行时消除虚函数表查找,并为该特定调用调用B.F();
。
B b;
b.F();
在下面的代码中,编译器将无法消除doSomething
中的运行时查找,但是它可能可以消除b.F()
中的查找。void doSomething( A* object ) {
object->F(); // will involve a vtable lookup
}
B b;
b.F(); // probably won't need a vtable lookup
doSomething( &b );
请注意,对象是在堆栈上还是在堆上分配并不重要。重要的是编译器能够确定类型。每个类仍然有一个虚函数表,只是不一定每次方法调用都需要它。
你提到代码内联,这与对象如何分配无关。当一个普通的函数被调用时,变量会和返回地址一起被压入堆栈。然后CPU将跳转到该函数。对于内联代码,函数调用的位置被替换为实际代码(类似于宏)。
如果继承层次结构中包含的对象在堆栈上分配,编译器仍然需要能够确定它可以调用哪些函数,特别是在存在虚函数和非虚函数的情况下。
- 是否应该使用继承来减少内存消耗的实例的内存分配?
- 动态分配的属性和继承
- 创建一系列超类的指针,以分配继承的类
- 如何将从C 模板继承的类超载分配运算符
- 复制构造函数继承动态分配的数组
- 如何<base>从多个继承的类对象分配unique_ptr?
- 避免使用组合进行额外的堆分配(过度继承)
- 为什么GCC警告使用STD ::元组和虚拟继承来调用非平凡的动作分配运算符
- 超载分配运算符的继承
- 正确的语法,用于在C 中继承,并具有初始化列表和内存分配
- 自定义内存分配和多个继承类的解除分配
- 分配同一类时上层类和继承类之间的区别
- 继承下基类的OOPS内存分配
- 在堆栈上分配对象时发生C++继承错误
- 如何查找继承类的分配地址
- 尝试分配值时C++继承中断的代码
- 在实现中,继承是来自连续分配的多个层的数据成员
- C++ABC,继承和虚函数的分配,试图找出一个段错误等等
- 静态分配继承对象的数组
- 无法从继承的类分配指向方法的指针