C++构造:"MyClass c"不好,"MyClass c = MyClass()"很慢,我想要"MyClass c()"

Ben*_*ker 12 c++ constructor

这是一些代码:

class MyClass
{
public:
    int y;
};

int main()
{
    MyClass item1;
    MyClass item2 = MyClass();
}
Run Code Online (Sandbox Code Playgroud)

当我运行它时,我收到以下值:

item1.y == [garbage]
item2.y == 0
Run Code Online (Sandbox Code Playgroud)

这让我感到惊讶.

我希望item1是默认构造的,而item2是从匿名默认构造的MyClass实例复制构造的,导致两者都等于0(因为默认构造函数将成员初始化为默认值).检查装配:

//MyClass item1;
//MyClass item2 = MyClass();
xor         eax,eax  
mov         dword ptr [ebp-128h],eax  
mov         ecx,dword ptr [ebp-128h]  
mov         dword ptr [item2],ecx  
Run Code Online (Sandbox Code Playgroud)

通过在某处临时写入'0'值然后将其复制到item2中来显示正在构造的item2,如预期的那样.但是,item1没有任何程序集.

因此,程序具有堆栈中item1的内存,但它从不构造 item1.

我很欣赏为了速度目的而想要这种行为,但我想要两全其美!我想知道item1.y == 0(它被构造),但我不想浪费时间在default-construct-anonymous-instance-then-copy-construct上,就像item2那样.

令人沮丧的是,我无法通过说明强制默认构造,MyClass item1();因为它被解释为函数原型.

所以...如果我想在item1上使用默认构造函数而不使用复制构造,那我该怎么做呢?

旁注:看起来如果我为MyClass声明一个构造函数,则item1像往常一样构造.所以这种行为只适用于编译器生成的构造函数.

Eva*_*ran 16

让你的课看起来像这样:

class MyClass 
{
public:
    MyClass() : y(0) 
    {
    }

public:
    int y;
};
Run Code Online (Sandbox Code Playgroud)

这两个例子都可以正常工作.您的问题是,如果没有提供构造函数,则不会初始化基本类型成员.因此y,表示任何随机数据恰好位于堆栈中的所在item1位置.

显式实现构造函数可以解决这个问题.

这种行为的存在是因为C++的"你只需支付你所使用的"心态.基本类型没有"默认值",因为这会使得分配某些东西不必要地(稍微)成本更高,然后由于有效地设置两次值而将其填入.一旦为"默认值",一次为您实际想要的值.

  • `item1`有一个名为 - 默认构造函数的构造函数.但是,该类中的默认构造函数不会对成员进行值初始化,而是默认初始化它们. (2认同)
  • 所以是的,显式构造函数是问题的一种解决方案,但它不是必需的.此外,通过定义一个默认构造函数,您正在抢夺自己的优化机会(没有初始化(尽管显然你想要小心)).通过显式使用'零初始化'编译器生成的默认构造函数,您可以获得相同的效果. (2认同)

Yak*_*ont 10

在C++中,您只需支付您要求完成的任务.具有不初始化其数据的类型的能力可以是一个重要的性能提升:令人讨厌的是,这也是默认行为,因为它是从C继承的.

你可以通过创建一个初始化零的构造函数来解决这个问题int,使用{}统一的初始化语法,或者int y = 0;在类型声明中使用新的默认语法(最后一个需要C++ 11,第二个需要C++ 11,如果类型是非POD).

Foo f = Foo();通过检查调试构建程序集,您的性能问题会被误导.市场上的每个非脑死亡编译器都支持复制省略这样一个微不足道的案例,即使ctor有副作用也是合法的.


Mar*_*ork 6

麻烦在于你对两件事的误解.

  1. 编译器生成的默认构造函数的作用.
  2. 什么是声明(因此在使用赋值时).

编译器生成的默认构造函数的作用.

如果您没有定义构造函数,编译器将为您生成默认构造函数.但是有两种不同的版本."值初始化"默认构造函数(对于内置POD类型不执行任何操作并使其未初始化)."零初始化"默认构造函数(对于内置POD类型,它们将它们设置为零).

什么是声明(因此在使用赋值时).

赋值运算符仅=在完全构造完左侧的对象时应用.由于声明中的对象直到';'才完全构造 这不是一项任务.

Bar x = Bar(); // There is no assignment here (this is a declaration using the default constructor
Bar y = Bar(2);// There is no assignment here (this is a declaration using a constructor).
Run Code Online (Sandbox Code Playgroud)

这是使用复制构造函数从临时构造对象.但这并不重要,因为编译器只是删除了实际副本并构建到位,所以如果发生任何副本,我会感到非常惊讶.

您的代码中发生了什么

int  x;          // default-Inititalized.      [ Value = Garbage]
int  z = int();  // Zero-Inititalized.         [ Value = 0]
Run Code Online (Sandbox Code Playgroud)

相同的规则适用于具有编译器生成的默认构造函数的类:

LokiClass  xL;               // Value-Initialized -> Default Initialized
                             //                     This is an explicit call to 
                             //                     the default constructor but  
                             //                     will only Value-Initialize
                             //                     class types and not initialize
                             //                     built-in POD types. 
LokiClass  yL = LokiClass(); // Zero-Initialized    This is an explicit call to 
                             //                     the default constructor but  
                             //                     makes sure to use the 
                             //                     Zero-Initialization version if
                             //                     it is the compiler generated 
                             //                     version.

LokiClass  y1L {};           // C++11 version of Zero-Initialization constructor used.

LokiClass  zL((LokiClass()));// This is copy construction
                             // Which will probably lead to copy elision by the compiler.
Run Code Online (Sandbox Code Playgroud)

那么Value/Zero Initialization有什么区别呢?
值初始化不会对内置POD类型进行初始化.它将在任何基类和成员上调用Value Initialization编译器生成的默认构造函数(注意,如果您定义了默认构造函数,那么它将使用它,因为没有编译器生成它).

零初始化将对POD类型进行零初始化.它将在任何基类和成员上调用Zero-Initialization编译器生成的默认构造函数(注意,如果您定义默认构造函数,那么它将使用它,因为没有编译器生成它).