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不是零初始化的(我想要实现的)。我是否理解正确,如果我定义了一个构造函数,它不会执行零初始化?(我知道如何使用显式默认值实现初始化为零。问题是为什么我不能使用初始化列表来做到这一点)

列表初始化

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

值初始化

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

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

聚合是以下类型之一:

  • 类类型(通常是结构或联合),具有
    • 没有用户声明的构造函数

什么是遗留代码的最简单且不易出错的修改,我需要解决在初始化之前使用某些类成员的问题?

4

3 回答 3

3

我是否理解正确,如果我定义了一个构造函数,它不会执行零初始化?

是的。

请注意,这T不是聚合,因为它包含用户提供的构造函数。作为值初始化的效果:

  1. 如果 T 是一个没有默认构造函数或用户提供或删除的默认构造函数的类类型,则该对象是默认初始化的;

  2. 如果 T 是具有既不是用户提供也不是删除的默认构造函数的类类型(即,它可能是具有隐式定义或默认构造函数的类),则该对象为零初始化,然后是默认值-如果它具有非平凡的默认构造函数,则初始化;

T包含用户提供的默认构造函数,然后应用#1(但不是首先执行零初始化的#2)。

默认初始化中,用户提供的默认构造函数用于初始化对象。默认构造函数不对数据成员执行初始化,它们被初始化为不确定的值。

于 2021-11-02T10:39:45.720 回答
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;
}
于 2021-11-02T09:55:24.337 回答
2

Zero initialization is a special case. The standard only guarantees member attributes to be default initialized. For class objects it indeed means that the default constructor will be called. But for basic type objects default initialization is just... no initialization at all. You have to explicitely ask for it if you need it:

T(): x(0), y(0) {
    ...
于 2021-11-02T10:07:14.320 回答