试图在std::unique_ptr上移动std::unique_ptr并将其传递给结构构造函数 &



我正在学习《c++速成班》这本书。作者J.Lospinoso,我现在正在第十一章介绍智能指针。

在第一个练习中,当使用std::unique_ptr时,读者必须使用文件处理编辑下面的代码:

#include <iostream>
#include <memory>
using FileGuard = std::unique_ptr<FILE, int(*)(FILE*)>;
void say_hello(FileGuard file){
fprintf(file.get(), "HELLO DAVE");
}
int main() {
auto file = fopen("HAL9000", "w");
if(!file) return errno;
FileGuard file_guard{ file, fclose };
//File open here
say_hello(std::move (file_guard));
//File closed here
return 0;
}

std::unique_ptr改为std::shared_ptr

在此之后,我们可以将file_guard传递给say_hello()函数而不需要std::move(),并对say_hello()进行更多的调用,一切都很好。

在下一个练习中,读者必须像下面这样写一个类,它已经被修改了一点:

#include <iostream>
#include <memory>
using FileGuard = std::shared_ptr<FILE>;
struct Hal {
Hal(std::shared_ptr<FILE> file):file(file){}
~Hal(){
fprintf(file.get(), "Stop, Daven");
}
void write_status(const char* ch, int len){
fprintf(file.get(), ch);
}
std::shared_ptr<FILE> file;
};
int main() {

auto file = fopen("HAL9000", "w");
if(!file) return errno;
//{
FileGuard file_guard{ file, fclose};
Hal hal1{std::move(file_guard)};
hal1.write_status("I'm completely operational1n", 8);
if (file) std::cout<<"File is not closedn";
//} 
fprintf(file, "I can still write after std::move..n"); 
return 0;
}

我的问题是,如果我将file_guard移动到Hal对象,为什么我仍然可以写入文件行"I can still write after std::move..n"?语法是否完全正确?

当取消注释main()中的大括号以创建作用域时,我不能再访问文件了,因为我希望fclose()hal1对象被破坏时被调用,不是吗?

我知道有很多C,但这就是这本书的写作方式,至少在某些地方。

使用MSVC 19.33.31629在Windows 11上编译。


编辑:

int main() {

auto file = fopen("HAL9000", "w");
if(!file) return errno;
FileGuard file_guard{std::move(file), fclose};
{
Hal hal1{file_guard}; //adding std::move causing undef behaviour? Nothing saved to file anyway.
hal1.write_status("I'm completely operational1n", 8);
}
Hal hal2{file_guard};
hal2.write_status("I'm completely operational2n", 8);
fprintf(file, "I can still write after std::move..n"); 
return 0;
}

在第一个示例中,您将move放入say_hello的参数中,但是触发文件保护的析构函数运行并关闭文件的是say_hello末尾的},而不是move

同样,

Hal hal1{std::move(file_guard)};

不会导致file_guard的析构函数运行,也不会以其他方式关闭文件。只要有一个指向文件的shared_ptr活动,文件就保持打开状态。从一个共享指针移到另一个共享指针的构造不会改变指向对象的引用计数,因此永远不会运行指向对象的析构函数。只有当hal1的析构函数在main的末尾运行时,它所拥有的shared_ptr才会被析构并关闭文件,所以在fprintf的时候文件仍然是打开的。

如果取消那些花括号的注释,hal1的析构函数将在结束的}处运行。在关闭的文件上调用fprintf是未定义的行为,因此程序是未定义的,实际发生的情况取决于你的编译器。

最新更新