考虑以下代码:
#include <memory>
#include <iostream>
#include <string>
// I know this is violationg rule of 5
class ElfData {
private:
std::string name;
public:
ElfData(std::string aName) : name(aName) {
std::cout<< name+" Constructor is called\n";
}
~ElfData() {
std::cout<< name+" Destructor is called \n";
}
};
int main() {
std::unique_ptr<ElfData> ptr1(new ElfData("Ptr1"));
std::unique_ptr<ElfData> ptr2(new ElfData("Ptr2"));
// Will this always call the destructor of object owned by ptr2 ?
ptr2 = std::move(ptr1);
return 0;
}
“
ptr2 = std::move(ptr1)
”行是否总是调用 ptr2 以前拥有的对象的析构函数?如果这是真的,我们可以替换下面的代码吗:
std::unique_ptr<ElfData> ptr1(new ElfData("Ptr1"));
ptr2 = std::move(ptr1);
与
ptr2.reset(new ElfData("Ptr1"));
?
我尝试在 cpp 参考文档和其他论坛中的类似问题中搜索,但我无法弄清楚这一点。
godbolt 链接:https://godbolt.org/z/Ynja8zaej
为了演示,我扩展了你的例子并添加了一个简单的包装器 围绕 unique_ptr 进行演示:
#include <memory>
#include <iostream>
#include <string>
struct Data {
std::string name;
Data(std::string aName) : name(aName) {
std::cout<< name << " Constructor is called\n";
}
~Data() {
std::cout<< name << " Destructor is called\n";
}
};
struct DataPtr{
std::unique_ptr<Data> ptr;
DataPtr(Data* data): ptr(data){}
void operator=(DataPtr&& v){
std::cout << "unique_ptr to " << ptr->name << " reassigned to " << v.ptr->name << "\n";
ptr = std::move(v.ptr);
}
};
int main() {
DataPtr ptr1(new Data("Data1"));
DataPtr ptr2(new Data("Data2"));
ptr2 = std::move(ptr1);
std::cout<< "End of Scope\n";
return 0;
}
这会产生以下输出:
Data1 Constructor is called
Data2 Constructor is called
unique_ptr to Data2 reassigned to Data1
Data2 Destructor is called
End of Scope
Data1 Destructor is called
(https://godbolt.org/z/nnK11nfq3)
如您所见,在 ptr2 被重新分配的那一刻, Data2 被破坏, 因为包含它的唯一指针被重新分配了。 (它的移动赋值运算符被调用,它在先前包含的对象上调用
delete
)。
如果你仔细想想什么对象超出了范围,这种行为对你来说应该是有意义的。
值得注意的是,这实际上是移动赋值运算符被调用的结果,not
std::move
,它只是将引用的类型重新解释为右值引用,但没有其他副作用。