创建新类的开销

wro*_*ame 21 c++ int optimization class

如果我有一个如此定义的类:

class classWithInt
{
public:
    classWithInt();
...
private:
    int someInt;
...
}
Run Code Online (Sandbox Code Playgroud)

someInt是一个且只有一个成员变量classWithInt,它慢多少.将宣布这个类,而不是只需要声明一个新的整数的新实例?

你有什么时候,在课堂上说10个这样的整数?100?

Kar*_*tel 43

由于编译器不是在凌晨时分醉酒的大学生写的,开销是零.至少在你开始投入virtual功能之前; 那么你必须支付虚拟调度机制的费用.或者如果类中没有数据,在这种情况下,类仍然需要占用一些空间(这又是因为每个对象在内存中必须有唯一的地址).

函数不是对象的数据布局的一部分.它们只是对象心理概念的一部分.该函数被转换为代码,该代码将对象的实例作为附加参数,并且相应地转换对成员函数的调用以传递该对象.

数据成员的数量无关紧要.比较苹果和苹果; 如果你有一个10个整数的类,那么它占用10个整数的相同空间.

无论它们是什么,在堆栈上分配东西都是免费的.编译器将所有局部变量的大小相加,并一次调整堆栈指针以为它们腾出空间.在内存成本中分配空间,但成本可能更多地取决于分配数量而不是分配的空间量.

  • 精神状态成本。被调用的构造函数需要函数调用,这在 CPU 周期方面并不是免费的。您已经提到了函数,但我认为您应该单独解释构造函数部分,因为构造函数存在于问题中并且不是内联的。 (2认同)
  • 只能在头文件中实现函数.问题中没有内联实现.构造函数可以在单独的编译模块中实现.构建操作根本不重要,我的观点是构造函数调用自身成本.使用内联函数可以避免心理状态成本.同样重要的是要记住,这样的优化仅在重负载应用程序中才有意义. (2认同)

Sin*_*ion 7

好吧,让我们全力以赴.我可以通过完全优化来编译一个更完整的示例,如下所示:

void use(int &);

class classWithInt
{
public:
    classWithInt() : someInt(){}
    int someInt;
};
class podWithInt
{
public:
    int someInt;
};

int main() {
  int foo;
  classWithInt bar;
  podWithInt baz;

  use(foo);
  use(bar.someInt);
  use(baz.someInt);

  return 5;

}
Run Code Online (Sandbox Code Playgroud)

这是我从gcc-llvm得到的输出

; ModuleID = '/tmp/webcompile/_21792_0.bc'
target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64"
target triple = "x86_64-linux-gnu"

%struct.classWithInt = type { i32 }

define i32 @main() {
entry:
  %foo = alloca i32, align 4                      ; <i32*> [#uses=1]
  %bar = alloca %struct.classWithInt, align 8     ; <%struct.classWithInt*> [#uses=1]
  %baz = alloca %struct.classWithInt, align 8     ; <%struct.classWithInt*> [#uses=1]
  %0 = getelementptr inbounds %struct.classWithInt* %bar, i64 0, i32 0 ; <i32*> [#uses=2]
  store i32 0, i32* %0, align 8
  call void @_Z3useRi(i32* %foo)
  call void @_Z3useRi(i32* %0)
  %1 = getelementptr inbounds %struct.classWithInt* %baz, i64 0, i32 0 ; <i32*> [#uses=1]
  call void @_Z3useRi(i32* %1)
  ret i32 5
}

declare void @_Z3useRi(i32*)
Run Code Online (Sandbox Code Playgroud)

每种情况都有一些差异.在最简单的情况下,POD类型与plain int的区别仅在于一种方式,它需要不同的对齐方式,它是8字节对齐而不是仅仅4字节.

另一个值得注意的事情是POD和裸int没有被初始化.它们的存储空间正好从堆栈的荒野中使用.非pod类型具有非平凡的构造函数,在实例可用于其他任何操作之前,会导致存储零.

  • 我完全不知道如何阅读此输出. (2认同)