使用时,你有什么方法可以用脚射击自己boost::shared_ptr?换句话说,当我使用时,我必须避免哪些陷阱boost::shared_ptr?
考虑用来解释一下这个典型的例子并不与前向声明做:
//in Handle.h file
class Body;
class Handle
{
public:
Handle();
~Handle() {delete impl_;}
//....
private:
Body *impl_;
};
//---------------------------------------
//in Handle.cpp file
#include "Handle.h"
class Body
{
//Non-trivial destructor here
public:
~Body () {//Do a lot of things...}
};
Handle::Handle () : impl_(new Body) {}
//---------------------------------------
//in Handle_user.cpp client code:
#include "Handle.h"
//... in some function...
{
Handle handleObj;
//Do smtg with handleObj...
//handleObj now reaches end-of-life, and BUM: Undefined behaviour
}
Run Code Online (Sandbox Code Playgroud)
我从标准中了解到这个案例正朝向UB,因为Body的析构函数是非常重要的.我想要了解的是这个的根本原因.
我的意思是,问题似乎是由Handle的dtor内联的事实"触发",因此编译器执行类似下面的"内联扩展"(这里几乎是伪代码).
inline Handle::~Handle()
{ …Run Code Online (Sandbox Code Playgroud) c++ destructor memory-management forward-declaration delete-operator
有时,实例化具有不完整类型的标准容器以获取递归结构是有用的:
struct multi_tree_node { // Does work in most implementations
std::vector< multi_tree_node > child;
};
struct trie_node { // Does not work in most implementations
std::map< char, trie_node > next;
};
Run Code Online (Sandbox Code Playgroud)
这往往起作用,因为容器没有按值value_type传递或返回任何value_type对象的类型或成员函数的成员.标准似乎并没有说明不完整的模板参数,但在C++11§17.6.4.8[lib.res.on.functions],"其他函数的要求"下有一点:
特别是,在以下情况下,效果未定义:...如果在实例化模板组件时将不完整类型(3.9)用作模板参数,除非特别允许该组件.
这是否使上述构造非法,即使实例化不在块范围内?这属于"用于实例化标准库模板组件的类型的操作"(也是17.6.4.8)吗?或者是一个库实现被禁止招致模板实例化,当所有特别需要的实例化成功时,这些实例化可能会因不完整类型而失败?
编辑:由于只有函数可以调用和实例化其他函数,因此将"对类型的操作..."限制为块范围内的操作似乎会将成员函数的内容保持为比签名和成员类定义的内容更严格的要求.毕竟,那肯定是没有意义的,以做任何事情与multi_tree_node之前型号齐全.并且这扩展到std::unique_ptr显式支持不完整类型参数的内容,即使在块作用域中使用也是如此.
编辑2:为了不打扰测试这个trie_node例子而为我服务 - 我以前甚至尝试过.它与@Ise链接的文章中的破损示例相同.然而,虽然这篇文章似乎理所当然地认为"没有什么可以起作用",但解决方案对我来说似乎很简单 - std::map内部tree_node类应该是非成员模板,而不是成员非模板类.
无论如何,那篇文章很好地建立了设计意图,所以我想我的挑剔是关于"功能要求"的副标题只是那样.
我有一个由Graph和Node类实现的有向无环图.每个节点都有一个指向childern的指针列表和一个指向父节点的指针列表.我最近添加了父母,因为有些算法要求快速访问父列表,图表很小,每个节点只有几个连接,所以没有内存问题.
子列表使用std :: shared_ptr,以便节点至少与父节点一样保留在内存中.但我不希望节点拥有其父节点,所以我使用weak_ptr作为指向父节点的指针.
但是那时算法存在问题.算法必须从weak_ptr创建一个新的shared_ptr,所以我不能直接使用operator ==,并且使用标准函数如std :: find()需要编写一个名为my_weak_ptr.lock()的lambda函数然后进行比较到某些shared_ptr.
如果我切换到shared_ptr,代码中任何可删除节点的小错误都可能导致内存泄漏.或者如果我有一个已经删除的节点的指针,代码将能够访问不应该存在的节点,因此找到一些错误可能会变得更加困难.但是,在不解除引用/删除/等方面,使用shared_ptr与weak_ptr一样安全.什么时候不应该,(因此它比原始的C++指针更好)和std :: find()可以直接使用,因为与weak_ptr不同,shared_ptr可以被解引用.
这里是否有"更好"的设计,或者这是一个特定情况的问题,具体取决于例如,如果我执行weak_ptr :: lock()的额外操作或者冒险难以发现错误,这有多重要?