【问题标题】:Why is a C++ class's destructor called after assigning using a converting constructor?为什么在使用转换构造函数赋值后调用 C++ 类的析构函数?
【发布时间】:2020-10-29 11:50:28
【问题描述】:

鉴于这个类:

class Baz
{
public:
    Baz() : Baz(0) {}
    Baz(int i) { _i = i; }
    Baz(Baz const &b) { _i = b._i * 10; }
    ~Baz() { }
private:
    int _i;
};

如果我调试并单步执行此代码:

Baz a = 4;

正如预期的那样,调用了采用intBaz 构造函数,我得到了一个Baz_i 为4。这很好。

但如果我这样做:

Baz b;
b = 4;

正如预期的那样,第一行调用了默认构造函数。第二行调用int 构造函数然后调用析构函数

我的第一个期望是第二个示例的第二行将简单地调用int 构造函数来分配给b。我没想到会调用析构函数,但如果它在赋值之前首先将整数4 转换为Baz,那么之后销毁它是有意义的。但是我希望在将该临时值分配给b 时调用复制构造函数。被销毁的Baz 具有4 的值_i,因此它不是在第一行创建的对象。

这里到底发生了什么?为什么这两种情况有区别?

【问题讨论】:

标签: c++ class constructor destructor copy-constructor


【解决方案1】:

这一行:

b = 4;

首先调用Bazint构造函数,构造一个临时的Baz,值为4。这个临时在分号处被销毁。

赋值不调用拷贝构造函数,而是实际调用拷贝赋值运算符,其签名为Baz& operator=(Baz const &);

【讨论】:

    【解决方案2】:

    这一行:

    Baz a = 4;
    

    相当于:

    Baz a(4);   // It simply constructs the object.
                // Note the `=` is not an assignment when
                // used in a declaration like this. It is
                // simply short hand (syntactic sugar) for
                // a single argument constructor.
    

    虽然这两行:

    Baz b;      // Default construct thus setting _i to zero.
    b = 4;      // This **IS** an assignment and needs an assignment
                // operator (or there is a compiler error).
    
                // Note there is no assignment operator that takes
                //      an integer. But there is a default assignment
                //      operator that takes a Baz by const reference.
                //
                //      This is auto generated by the compiler.
                //      See rule of 5
                //         Baz& operator=(Baz const& copy): _i(copy._i){return *this)
                //
                // So the compiler must convert the integer to Baz before 
                // an assignment is allowed. The compiler is allowed to 
                // create an Object using a one parameter constructor so
                // that line is equivalent too:
    
      // Equiv
      b = Baz(4);  // So this creates a temporary Baz object here.
                   // Then the assignment operator is called.
                   // Then at the end of the statement the temporary
                   // object must be destroyed so we call the destructor  
                   // on the temporary.
    

    【讨论】:

      【解决方案3】:

      语句Baz a = 4;只是Baz a(4);的语法糖,所以它直接调用Baz(int)转换构造函数。

      Baz 没有定义operator=(int),但它有一个operator=(const Baz&)(在您的示例中由编译器生成)。而Baz 有一个Baz(int) 构造函数。 const Baz& 参数可以绑定到右值,并且从 intBaz 的隐式转换只有 1 次。因此,编译器能够将语句b = 4; 视为b = Baz(4);,即它创建一个临时的Baz 对象并将其传递给operator=。临时在创建它的语句结束时被销毁(即在;)。

      所以,您看到的是临时 Baz 对象的 int 构造函数和析构函数。

      【讨论】:

        【解决方案4】:

        构造函数只能用于创建新对象。

        b = 4; 不会创建b,因此它不能在b 上调用构造函数。相反,它使用Baz(int i) 构造函数创建一个临时的Baz 实例,然后将临时分配给b(使用operator=,在您的情况下由编译器生成)。

        临时在完整表达式的末尾被销毁(即当控制到达;时),这就是你看到析构函数调用的原因。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2014-04-22
          • 2019-11-16
          • 2021-07-19
          • 1970-01-01
          • 2021-04-01
          • 2011-05-21
          • 1970-01-01
          相关资源
          最近更新 更多