【问题标题】:Why primitive to class type conversion destroys object values?为什么原始到类类型的转换会破坏对象值?
【发布时间】:2022-01-25 00:40:00
【问题描述】:

为什么在下面的代码中设置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 = 10; 行中创建了一个新对象(通过隐式转换构造函数)——显然,新对象对任何先前对象的值一无所知

标签: c++ c++11 constructor


【解决方案1】:

这相当于

c1 = abc(10);

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


构造函数abc()也是如此,这里两个成员变量都没有初始化,所以它们都包含“垃圾”值。当您在施工后立即致电showdata 时,您会看到这一点

abc c1;
c1.showdata();

【讨论】:

    【解决方案2】:

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

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

    之后,临时对象被销毁。

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

    【讨论】:

      【解决方案3】:

      你写的时候:

      c1 = 10;
      

      在右侧,converting constructor用于创建abc类型的临时对象。

      接下来赋值运算符用于将右侧的临时对象分配给左侧的对象。

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

      这就是为什么建议这样做

      始终在本地/块范围内初始化内置类型。

      解决这个问题,您可以使用类内初始化器,如下所示:

      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
          }
      };
      

      【讨论】:

      • “赋值运算符”不!移动任务被选中!并且 var a 没有被初始化而是被覆盖了!
      • @Klaus 首先,我说使用了赋值运算符是对的。只是我没有提到将使用哪个赋值运算符(在复制赋值运算符或移动赋值运算符中)。而且我认为这里没有必要提及这一点。其次,数据成员a被赋值而不是初始化,我同意这一点。
      【解决方案4】:

      你的线路

      c1 = 10; 
      

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

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

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

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

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

      【讨论】:

        猜你喜欢
        • 2020-02-10
        • 2013-01-12
        • 1970-01-01
        • 2018-07-29
        • 1970-01-01
        • 2015-12-19
        • 1970-01-01
        • 1970-01-01
        • 2011-05-01
        相关资源
        最近更新 更多