Ziq*_*Liu 1 c++ shared-ptr c++11
如果 classA包含 class B,那么在A析构时,B会先调用 \ 的析构函数,即它们的嵌套关系的顺序相反。
但是,如果A包含一个shared_ptrof B,而B包含一个指向 的原始指针A,我们应该如何处理析构函数以使其安全?
考虑以下示例:
\n\n#include <iostream>\n#include <memory>\n#include <unistd.h>\n\nstruct B;\nstruct A {\n int i = 1;\n std::shared_ptr<B> b;\n\n A() : b(std::make_shared<B>(this)) {}\n\n ~A() {\n b = nullptr;\n std::cout << "A destruct done" << std::endl;\n }\n};\n\nstruct B {\n A *a;\n\n B(A *aa) : a(aa) {}\n\n ~B() {\n usleep(2000000);\n std::cout << "value in A: " << a->i << std::endl;\n std::cout << "B destruct done" << std::endl;\n }\n};\n\nint main() {\n std::cout << "Hello, World!" << std::endl;\n {\n A a;\n }\n std::cout << "done\\n";\n return 0;\n}\n\nRun Code Online (Sandbox Code Playgroud)\n\nA你可以在\的析构函数中看到,我显式地将b设置为nullptr,这将立即触发B\的析构函数,并阻塞直到它完成。\n输出将为\xef\xbc\x9a
Hello, World!\nvalue in A: 1\nB destruct done\nA destruct done\ndone\nRun Code Online (Sandbox Code Playgroud)\n\n但如果我注释掉那行
\n\n ~A() {\n // b = nullptr; // <---\n std::cout << "A destruct done" << std::endl;\n }\nRun Code Online (Sandbox Code Playgroud)\n\n输出将是:
\n\nHello, World!\nA destruct done\nvalue in A: 1\nB destruct done\ndone\n\nRun Code Online (Sandbox Code Playgroud)\n\n看来A\ 的析构函数无需等待B析构就完成了。但在这种情况下,我预期段错误,因为当A已经被破坏时,B尝试访问 的成员A,这是无效的。但为什么程序不会产生段错误呢?碰巧没问题吗(即,undefined behavior)?
另外,当我改变时
\n\n {\n A a;\n }\nRun Code Online (Sandbox Code Playgroud)\n\n到
\n\n A * a = new A();\n delete a;\nRun Code Online (Sandbox Code Playgroud)\n\n输出仍然相同,没有段故障。
\n准确了解正在发生的事情非常重要。当A被销毁时,以下事件按以下顺序发生:
A::~A()叫做。A的生命周期结束。该对象仍然存在,但不再处于其生命周期内。([基本生活]/1.3)A::~A()被执行。A::~A()A以相反的声明顺序隐式调用直接非静态成员的析构函数([class.dtor]/9,[class.base.init]/13.3)A::~A()返回。A对象不再存在([class.dtor]/16)。它曾经占用的内存成为“已分配存储”([basic.life]/6),直到被释放为止。(所有引用均参考 C++17 标准)。
在析构函数的第二个版本中:
~A() {
std::cout << "A destruct done" << std::endl;
}
Run Code Online (Sandbox Code Playgroud)
打印该语句后,该成员b被销毁,这会导致所拥有的B对象被销毁。此时,i尚未被销毁,因此可以安全地访问它。之后,B析构函数返回。然后,i被“销毁”(有关一些微妙之处,请参阅CWG 2256 )。最后,返回析构函数A。到那时,尝试访问该成员将不再合法i。
| 归档时间: |
|
| 查看次数: |
6021 次 |
| 最近记录: |