使用复制列表初始化在C++中进行对象初始化

4
// Example program
#include <iostream>
#include <string>

class T{
public:   
    int x, y;
    T(){
        std::cout << "T() constr called..." << std::endl;
    };
    T(int x, int y):x(x),y(y){
        std::cout << "T(x,y) constr called..." << std::endl;
    }
    
    void inspect(){
        std::cout << "T.x: " << this->x << std::endl;
        std::cout << "T.y: " << this->y << std::endl;
    }
};

int main()
{
    T t1(5,6);
    t1.inspect();
    
    std::cout << std::endl;
    
    T t2 = {};
    t2.inspect();
}

我收到如下结果:
T(x,y) constr called...
T.x: 5
T.y: 6

T() constr called...
T.x: 208787120
T.y: 31385
t2实例的成员没有被零初始化(这正是我想要实现的)。我的理解是否正确,如果我定义了构造函数,它将不执行零初始化? (我知道如何使用显式默认值实现初始化为零。问题在于我为什么无法使用init-list实现它)
列表初始化

否则,如果花括号初始化列表为空,且T是具有默认构造函数的类类型,则执行值初始化。

值初始化

在所有情况下,如果使用空括号对{}并且T是聚合类型,则执行聚合初始化,而不执行值初始化。

聚合初始化(似乎这不是我的情况,因此它没有将成员初始化为零)

聚合体是以下类型之一:

  • 没有用户声明的构造函数的类类型(通常是结构体或联合体)
对于需要解决某些类成员在初始化之前被使用的遗留代码,最简单且最少出错的修改方式是什么?

使用 T() = default; 进行初始化会更简单/更安全(而使用 T t3; 则可能导致垃圾值或未定义行为),因此最好使用 int x = 0; int y = 0; 进行初始化。 - Jarod42
离题:选择类名 T 不是一个好的选择。它对类的功能一无所知,而且 T 通常与模板类型参数相关联。 - Marek R
3个回答

3
t2的数据成员具有“垃圾值”。这是因为它们属于内置类型,且您未显式初始化它们。解决方案如下: 解决方案1:使用构造函数的初始化列表。
T(): x(0), y(0){
        std::cout << "T() constr called..." << std::endl;
    };

解决方案2: 使用类内初始化器

int x = 0, y = 0;

这就是为什么建议在块/局部作用域中始终初始化内置类型的原因。
如果您使用上述任何一种解决方案,则输出将为:
T(x,y) constr called...
T.x: 5
T.y: 6

T() constr called...
T.x: 0
T.y: 0

这是您想要并且可以在这里这里看到的。

另一种解决方案是使用委托构造函数(如下评论中@MarekR所建议):

T():T(0, 0) 
{
    std::cout << "T() constr called..." << std::endl;
}

解决方案3:委托构造函数:T():T(0, 0) {} - Marek R
@MarekR 是的,我现在已经在我的答案末尾添加了它。请查看一下。 - Jason

3
我是否正确地理解,如果我定义了一个构造函数,它将不执行零初始化吗?
是的。
请注意,因为它包含用户提供的构造函数,所以T不是聚合体。作为值初始化的效果:
1. 如果T是没有默认构造函数或有用户提供的或已删除的默认构造函数的类类型,则对象会被默认初始化; 2. 如果T是具有既非用户提供也非已删除的默认构造函数(也就是说,它可能是具有隐式定义或默认定义的默认构造函数的类),则对象将被零初始化,然后如果它具有非平凡的默认构造函数,则对其进行默认初始化; T包含用户提供的默认构造函数,那么首先执行#2的零初始化,然后再应用#1。
默认初始化中,使用用户提供的默认构造函数来初始化对象。默认构造函数不会对数据成员进行初始化,它们将被初始化为不确定的值。

2

零初始化是一种特殊情况。标准仅保证成员属性被默认初始化。对于类对象,这确实意味着将调用默认构造函数。但对于基本类型对象,默认初始化只是……根本没有初始化。如果需要它,您必须显式地请求:

T(): x(0), y(0) {
    ...

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接