我正在学习《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
是未定义的行为,因此程序是未定义的,实际发生的情况取决于你的编译器。