代码之家  ›  专栏  ›  技术社区  ›  Galaxy

为什么在缺少赋值运算符的情况下调用转换构造函数?

  •  0
  • Galaxy  · 技术社区  · 6 年前

    今天我的问题是,为什么在没有用户提供的赋值运算符的情况下调用构造函数?我知道构造函数和赋值运算符在 = 符号。如果对象在该行之前不存在,则隐式调用构造函数。

    std::string name = "Bjarne"; // implicit constructor

    如果对象在该行之前已经存在,则调用赋值运算符。

    std::string name;
    name = "Bjarne";   // assignment operator
    

    现在我有一个 class Number 这里,这是一个带标记的联合。它只是一个数字,表示 int 或A float . 我定义了多个转换构造函数,使用户可以轻松地将值分配给 Number 隐含地。我希望他们在这种情况下被召唤:

    Number num1 = 8;

    为了确定何时调用哪个构造函数,我将print语句放在构造函数的主体中。

    #include <cstdlib>
    #include <iostream>
    
    using std::cin;
    using std::cout;
    using std::cerr;
    using std::endl;
    using std::ostream;
    
    
    /* Unions can be anonymous (unnamed).
     * This form is used when nesting a union inside a struct or class that contains an extra data member
     * to indicate what the union contains.
     * This arrangement is called a tagged union (or managed union).
     * It can be used as a "polymorphic" class which can be several things at once, for example to create an array
     * of different data types.
     */
    
    class Number {
        friend ostream& operator<<(ostream& os, Number& num);
      public:
        Number(int i = 0) : DATA_TYPE(INTEGER), i(i) { cerr << "Number(" << i << ")\n"; }
    
        Number(unsigned int u) : DATA_TYPE(INTEGER), i(static_cast<int>(u)) { cerr << "Number((signed) " << i << ")\n"; }
    
        Number(float f) : DATA_TYPE(FLOAT), f(f) { cerr << "Number(" << f << ")\n"; }
    
        Number(double d) : DATA_TYPE(FLOAT), f(static_cast<float>(d)) { cerr << "Number((float) " << f << ")\n"; }    
    
        Number(const Number& rhs) : DATA_TYPE(rhs.DATA_TYPE), i(rhs.i) { cerr << "Number((Number) " << *this << ")\n"; }
      private:
            enum data_type { INTEGER, FLOAT } DATA_TYPE;
    
        union {
            int i;
            float f;
        };
    };
    
    
    ostream& operator<<(ostream& os, Number& num)
    {
        switch (num.DATA_TYPE) {
            case Number::INTEGER:
          cout << num.i;
              break;
            case Number::FLOAT:
          cout << num.f;
              break;
        default:
          cout << 0;
          break;
        }
    
        return os;
    }
    
    
    int main()
    {
        Number num1 = 5;  // conversion constructor
        cout << num1 << endl;
        num1 = 12.5;
        cout << num1 << endl;
    
        return EXIT_SUCCESS;
    }
    

    当我运行此代码时,它会为我生成意外的输出:

    Number(5)
    5
    Number((float) 12.5)
    12.5
    

    我看到了什么时候 Number num1 初始化时,构造函数 int 作为参数调用。然后我打印 num1 . 然后在下一行中,似乎构造函数 double 作为参数调用。这很奇怪。在那条线之前, NUM1 已作为对象存在。所以它应该在逻辑上调用赋值操作符。我没有定义赋值运算符,因此我假定将调用编译器生成的赋值运算符。

    我想知道,在没有用户提供的分配运算符的情况下,当我这样做时会发生什么? num1 = something; 编译器提供的默认赋值运算符是被调用的还是被调用的构造函数?当对已经存在的对象调用构造函数时会发生什么?那个物体被“重建”了吗?那么在这种情况下实际发生了什么?

    2 回复  |  直到 6 年前
        1
  •  2
  •   Jarod42    6 年前

    我想知道,在没有用户提供的分配运算符的情况下,当我这样做时会发生什么情况 num1 = something; ?

    num1 = 12.5;
    

    隐调用 Number::operator =(const Number&) .

    所以构造一个临时数字 double .

    编译器提供的默认赋值运算符是被调用的还是被调用的构造函数?

    两者都有。为临时对象而不是现有对象调用构造函数。

    当对已经存在的对象调用构造函数时会发生什么? 那个物体被“重建”了吗?那么在这种情况下实际发生了什么?

    不能在现有对象上调用构造函数。你能做的最近的事是 placement-new .

        2
  •  0
  •   Galaxy    6 年前

    多亏了Jarod42,我才明白。正如他所建议的,暂时的 Number 物体被无形地建造在 = ,然后调用赋值运算符,在该行的末尾销毁临时对象。

    我将以下代码添加到 来看看到底发生了什么。

    Number& operator=(const Number& rhs)
    {
        cerr << "operator=(const Number&)" << endl;
    return *this;
    }
    
    ~Number() { cerr << "~Number()" << endl; }
    

    用这个 main() 功能

    int main()
    {
        Number num1 = 5;
        num1 = 12.5;
    
        cout << endl;
    
        Number num2 = 8;
        num1 = num2;
    
        return EXIT_SUCCESS;
    }
    

    产生以下输出:

    Number(5)
    Number((float) 12.5)
    operator=(const Number&)
    ~Number()
    
    Number(8)
    operator=(const Number&)
    ~Number()
    ~Number()