为什么从原始类型到 class 类型的转换会破坏对象值?

Why primitive to class type conversion destroys object values?

为什么在下面的代码中,当我设置 c1 = 10 时,会破坏对象的变量 (abc) 的所有其他值。该语句应调用构造函数,并且在定义构造函数时将 a 的值设置为 10,但是当我尝试访问 bc 的值时;它给了我垃圾值。

#include<iostream>
using namespace std;
class abc{
    private:
    // properties
    int a,b, c;
    
    public:
    
    void setdata(int x,int y)
    {
        a = x;
        b = y;
    }
    void showdata(){
        cout << "a = " << a << " b = " << b << "\n";
    }
    
    // constructors
    abc(){}
    
    abc(int k)
    {
        a=k;
    }
};
int main()
{
    
abc c1; // object intialization
c1.setdata(6,7); // setting values of properties
c1.showdata(); // printing values

c1 = 10; // primitive to class type conversion, constructor is being called
c1.showdata(); // why value of b and other variables is getting changed ?

return 0;

}

这相当于

c1 = abc(10);

构造函数abc(int k)没有初始化值bc,因此成员变量包含一些随机值。这些来自临时 abc(10) 对象的随机值随后被复制到 c1.


构造函数也是如此abc(),这里两个成员变量都没有初始化,所以都是“垃圾”值。当你在构建

后立即调用 showdata 时,你会看到这一点
abc c1;
c1.showdata();

当你写道:

c1 = 10;

在右侧,转换构造函数用于创建临时类型abc对象。

Next赋值运算符用于将右侧的临时对象赋值给左侧的对象.

此外,请注意转换构造函数 abc(int k); 仅将 分配给数据成员 a 并保留数据成员 bc原样。但是由于您没有显式初始化成员 bc,它们具有 不确定值 。 using/accessing 这些值(bc)导致 未定义的行为

这就是为什么建议

always initialize built in types in local/block scope.

解决这个你可以使用in-class初始化器如下所示:

class abc{
    private:
    // USE IN-CLASS INITIALIZERS
    int a = 0,b = 0, c =0;
    
    public:
    
    void setdata(int x,int y)
    {
        a = x;
        b = y;
    }
    void showdata(){
        cout << "a = " << a << " b = " << b << "\n";
    }
    
    // constructors
    abc(){}
    
    abc(int k)
    {
        a=k;
    }
};

也不是在构造函数体内给数据成员a赋值,而是应该使用构造函数初始化列表来初始化它,如下所示:

class abc{
    private:
    // USE IN-CLASS INITIALIZERS
    int a = 0,b = 0, c =0;
    
    public:
    
    void setdata(int x,int y)
    {
        a = x;
        b = y;
    }
    void showdata(){
        cout << "a = " << a << " b = " << b << "\n";
    }
    
    // constructors
    abc(){}
    //use constructor initializer list
    abc(int k): a{k}
    {
       //no need to assign to `a` here
    }
};

你的线路

c1 = 10; 

创建一个新对象,其中只调用您的构造函数 abc(int k)。现在你有了一个临时对象,它将被移动到现有的对象中 c1。由于您只覆盖变量 a,所有其他变量都没有明确设置。

我不知道这是否是有意为之的行为,即您创建了一个临时对象,该对象将被移动到现有对象中。如果你不想要它,你应该让你的构造函数 explicit.

顺便说一句:您的构造函数没有初始化您的变量 a 而是覆盖了它。对于 int 类型,这无关紧要,但对于其他不可默认构造的类型,它将不起作用。

您应该始终使用构造函数初始化程序来初始化您的变量,例如:

abc(int k): a{k}{}

事实上 c1 = 10; 调用了 2 个不同的步骤:

  • 临时 abc 对象是从 abc(int k) 构造函数构造的 - 显然它的 b 和 c 值未设置
  • 然后使用将替换所有成员的移动赋值运算符之一将临时分配给 c1

之后,临时文件被销毁。

如果您有充分的理由不覆盖副本中的先前值,您可以提供自定义复制或移动赋值运算符。但请注意,每次作业都会用到它。