复制构造函数,赋值运算符或两者都在这里使用?

时间:2014-07-01 20:36:49

标签: c++

我有这段代码:

CFoo::CFoo()
{
    InitializeCriticalSection( &m_CriticalSection );
    m_IsInitialized = FALSE;
    m_CBar = CBar(15);
}

当CBar的实例构建在CFoo构造函数的第三行的右侧时,是否调用了复制构造函数将其移动到m_CBar,或者只是编译器调用的赋值运算符?

3 个答案:

答案 0 :(得分:5)

在构造函数的成员初始化列表中未显式初始化的任何类成员在输入构造函数体之前进行默认初始化。对于类类型的成员,这意味着默认构造:

CFoo::CFoo()
// : m_CBar()         <--------- The constructor behaves as if you wrote this*
{
    InitializeCriticalSection( &m_CriticalSection );
    m_IsInitialized = FALSE;
    m_CBar = CBar(15);
}

因此,在m_CBar = CBar(15);,您将CBar的临时实例(由CBar(15)构造)分配给m_CBar,后者将调用赋值运算符。

通常最好在成员初始化列表中构造m_CBar

CFoo::CFoo()
 : m_CBar(15)    // Directly constructs m_CBar, passing 15 to the CBar constructor
{
    InitializeCriticalSection( &m_CriticalSection );
    m_IsInitialized = FALSE;
}

* : m_CBar()实际上 value-initializes m_CBar,它与 default-initialization 在像这里一样,m_CBar是具有用户提供的构造函数的类类型的情况。在涉及没有用户提供的构造函数的标量类型或类的情况下, value-initialization 将导致零初始化(之后调用任何非平凡的隐式默认构造函数非联合类类型),而 default-initialization 不会导致零初始化。您可以在C ++ 11标准的§8.5[dcl.init]中找到所有三种初始化(零,默认和值)定义的所有有趣细节。

C ++ 14对这些规则做了一些修改,在某些边缘情况下插入了一些漏洞。但最重要的变化是,具有显式默认构造函数的对象现在始终与具有隐式定义的默认构造函数的对象一样(因此在默认构造函数之前始终首先进行零初始化,如果非常重要,则调用),无论如何是否还有其他用户提供的构造函数。

答案 1 :(得分:1)

您将看到首先调用构造函数CBar(15),然后您将看到对赋值运算符的调用(demo #1)。

请注意,如果您将作业作为声明的一部分,例如

CBar a = CBar(15);
由于demo #2,优化开启时很可能只有构造函数 - 不是构造函数+赋值(copy elision)。

答案 2 :(得分:1)

将你的代码段分开:

CFoo::CFoo()
{
    InitializeCriticalSection( &m_CriticalSection );

m_CriticalSection默认初始化(即未初始化,因为它是没有ctor的原始类型的结构)

    m_IsInitialized = FALSE;

Dito代表m_IsInitialized

    m_CBar = CBar(15);
默认初始化

m_CBar(default-ctor),然后构造临时值,然后分配,然后临时销毁。

}

如何减少工作量,请使用ctor初始化程序列表:

CFoo::CFoo() : m_IsInitialized(), m_CBar(15)
{
    InitializeCriticalSection( &m_CriticalSection );
}

在C ++ 11及更高版本中,您甚至可以将初始化程序放在成员声明中,如果未被被叫ctor的ctor init列表覆盖,它们将被使用。

Btw:在初始化程序的定义中如下:

CBar a = 1;

仅调用a的相应ctor 如果使用相同类型的临时文件初始化它,则副本可以(并且将在任何值得使用的编译器上)显示:

Cbar a = CBar(1);

或者:

CBar a = make_cbar(1,2,3);