构造函数初始化与赋值

Utt*_*kar 21 c++ constructor

让我们考虑以下几个类

class test1
{
    private:
        int a;
        int b;
    public:
        test1():a(0),b(0){}
};

class test2
{
    private:
        int a;
        int b;
    public:
        test2()
        {
            a=0;
            b=0;
        }
};
Run Code Online (Sandbox Code Playgroud)

现在,我知道这test1() constructor是初始化a的数据成员的正确方法class,因为test2()我们正在执行赋值而不是初始化.我的问题是:

  1. 如果我们执行赋值而不是初始化会出现什么问题?
  2. test1()构造函数的情况下,编译器是否在内部执行赋值?如果没有,那么这些是如何初始化的?

Mik*_*our 17

如果我们执行赋值而不是初始化会出现什么问题?

某些类类型(以及引用和const对象)无法分配; 有些不能默认初始化; 默认初始化和重新分配可能比直接初始化更昂贵.

在test1()构造函数的情况下,编译器是否在内部执行赋值?如果没有,那么这些是如何初始化的?

在原始类型的情况下,int两者之间几乎没有实际差异.默认初始化什么都不做,直接初始化和赋值都基本上做同样的事情.

在类类型的情况下,默认初始化,分配和直接初始化每个调用不同的用户定义函数,并且一些操作可能根本不存在; 所以一般来说,这两个例子可能会有非常不同的行为.


Con*_*shi 11

对于您的示例,没有真正的不同,因为您正在初始化普通整数.

但假设这些整数是带有构造函数的对象,那么编译器将生成以下调用:

// test1
a::copy_constructor(0);
b::copy_constructor(0);


// test2
a::default_constructor();
b::default_constructor();
a::operator = (0);
b::operator = (0);
Run Code Online (Sandbox Code Playgroud)

因此,根据您的对象,test2可能会对性能产生巨大影响.此外,通过在初始化列表中初始化对象,保证在输入构造函数时变量具有数据.初始化列表的一个"缺点"是它按照声明变量的顺序执行,而不是按初始化列表的顺序执行,因此可能是您不想使用初始化列表.


Roi*_*ton 6

第三种变体利用直接列表初始化。优点是

  1. 变量不必在构造函数中默认初始化
  2. 构造函数而不是复制/移动赋值运算符用于构造成员(正如其他答案所说,对于本示例的原始类型并不重要)

因此,这比问题中的两种变体更不容易出错:

#include <iostream>
class Test3
{
    public: // Made public so we can easily output the vars. No struct is used to be consistent with question.
        int a { 4 }; // No need for separate initialization in constructor.
        int b { 2 }; // No need for separate initialization in constructor.
};
int main()
{
    const auto t = std::move(Test3()); // Implicitly-declared default constructor + implicitly-declared move assignment operator
    std::cout << t.a << t.b;
}
Run Code Online (Sandbox Code Playgroud)

输出是42.