难以理解 C++ 构造函数和析构函数以及移动/复制语义

Having trouble understanding C++ constructors and destructors and move/copy semantics

提问人:Nikesh Subedi 提问时间:9/7/2022 最后编辑:Nikesh Subedi 更新时间:9/7/2022 访问量:57

问:

我有以下程序,我在其中测试构造函数和析构函数调用的顺序,我对输出感到惊讶。

#include <iostream>
#include <utility>


class Doctor {
    public:
    Doctor(){
        std::cout << "Doctor Creation\n";
    }

    ~Doctor(){
        std::cout << "Doctor DeCreation\n";
    }
};


class Nurse{
    public:
    int op = 0;
    Nurse(){
        std::cout << "Nurse Creation\n";
    }

    ~Nurse(){
        std::cout << "Nurse " <<  op << " DeCreation\n";
    }
};

class Hospital{
    private:
        Doctor doc;
        Nurse nurse;
    public:
    Hospital(){
        std::cout << "Hospital Creation\n";
    }
    ~Hospital(){
        std::cout << "Hospital DeCreation\n";
    }

    void operate(){
        std::cout << "Hospital Operation\n";
        nurse.op++;
        std::cout << &this->nurse << " " << nurse.op << "\n";
        reset_hospital();
        std::cout << &this->nurse << " " << nurse.op << "\n";
    }

    void reset_hospital(){
        std::cout << &this->nurse << " " << nurse.op << "\n";
        nurse = std::move(Nurse());
        doc = std::move(Doctor());
    }
};

int main(){
    Hospital hospital;
    hospital.operate();
}

https://godbolt.org/z/bf8KbqsTK

输出为:

Doctor Creation
Nurse Creation
Hospital Creation
Hospital Operation
0x7fff2316866c 1
0x7fff2316866c 1
Nurse Creation
Nurse 0 DeCreation
Doctor Creation
Doctor DeCreation
0x7fff2316866c 0
Hospital DeCreation
Nurse 0 DeCreation
Doctor DeCreation

它不应该在 std::move 操作上吗?当前对象被破坏并被新对象替换?还是出于某种原因,是复制而不是移动?Nurse 1 DeCreation

我很困惑。

C++ 移动 析构函数 复制构造函数

评论

0赞 user12002570 9/7/2022
C++应该用一本好的C++书来学习,而不是通过反复试验来学习。特别是,这在任何初学者 c++ 书籍中都有解释。目前尚不清楚您对什么感到困惑。另外,请参阅 std::move 实际上不会移动任何东西
0赞 doug 9/7/2022
在什么都不做。 已经是右值nurse = std::move(Nurse());std::moveNurse()
0赞 doug 9/7/2022
没有理由通过赋值将新护士对象复制(分配)到旧护士对象会调用 dtor。它只是将新胆量(即:op)复制到旧胆量上。
0赞 JohnFilleau 9/7/2022
@doug我相信 OP 希望复制分配实际上是移动分配,并希望该移动分配执行交换。
0赞 doug 9/7/2022
@JohnFilleau是的,我认为你是对的。它没有移动同化,如果有,它通常不会交换操作。我想他可以写一个带有交换的移动任务。这应该符合他的期望。

答:

1赞 JohnFilleau 9/7/2022 #1

Nurse 0 DeCreation而不是因为:Nurse 1 DeCreation

  1. Nurse没有移动赋值运算符(请参阅下面的说明),
  2. Nurse具有使用的复制分配运算符,并且
  3. 如果有一个隐式定义的移动赋值运算符,这将是微不足道的,并且成员将像 一样作,这不会交换基础成员Nursestd::memmoveop

移动语义不保证会发生交换。对于微不足道的可移动成员变量,赋值比交换效率高得多。移动语义不一定保证“moved-from”对象与“moved-to”对象的先前状态相同。移动语义仅(应)保证从中移动的对象处于有效状态。它可以是未指定的。

为什么没有移动分配运算符?

https://en.cppreference.com/w/cpp/language/move_assignment

如果没有为类类型(结构、类或联合)提供用户定义的移动赋值运算符,并且满足以下所有条件:

  • 没有用户声明的复制构造函数;
  • 没有用户声明的移动构造函数;
  • 没有用户声明的复制分配运算符;
  • 没有用户声明的析构函数,

然后编译器会将移动赋值运算符声明为其类的内联公共成员,签名为 T&T::operator=(T&&)。

Nurse 具有用户声明的析构函数,因此没有隐式声明的移动分配运算符

Nurse 具有隐式定义的复制分配运算符,并且其成员将被复制。第一个被破坏的对象有 ,所以你看 。Nurseop = 0Nurse 0 DecCreation

如果它确实有一个移动分配运算符,该怎么办?

如果未为用户提供,则将具有隐式声明的移动赋值运算符。它也是一个微不足道的移动赋值运算符,它只能保证像使用 .即逐字节复制。~Nurse()Nursestd::memmove