让我们采用以下代表子节点树的数据结构(Node)示例。每个对象的子节点集存储在映射>
中class Node;
typedef std::shared_ptr<Node> NodePtr;
class Node
{
std::map<const std::string, NodePtr> _childNodes;
void SomeOtherMethod();
public:
bool GetChildByKeyName(/*In*/ const std::string& key, /*Out*/ NodePtr& spChild)
{
bool result = false;
auto itor = _childNodes.find(key);
if (itor != _childNodes.end())
{
spChild = itor->second;
result = true;
SomeOtherMethod();
}
return result;
}
};
以下代码示例作为用户通常会调用它。
NodePtr spNode, spChildNode;
bool result;
...
result = spNode->GetChildByKeyName(strChildKeyName, spChildNode);
到目前为止一切顺利。
我突然想到,调用者可以遍历树而不必为树中的每个深度处理额外的变量
NodePtr spNode;
bool result;
result = spNode->GetChildItem(strChildKeyName, spNode);
if (result)
spNode->GetChildItem(strSubKeyName, spNode);
在上面的例子中,如果spNode是对象的 final 剩余引用,那么我担心GetChildItem方法中的这段代码:
spChild = itor->second;
result = true;
SomeOtherMethod();
spChild(实际上是调用者的spNode实例)的赋值是否因为最后一个引用消失而无意中破坏了“this”节点? (因此在spChild赋值之后调用其他方法是危险的)。我这里有潜在的错误吗?
我认为解决方法是简单地在方法调用的顶部添加这一行:
NodePtr spChildRef = spChild; // maintain a reference to the caller's original node during the scope of the method
思想?
答案 0 :(得分:6)
如果第二个示例中最外面的spNode指针是对根项的唯一引用,那么你是正确的,GetChildByKeyName将替换该引用,导致该对象被破坏(实质上是“删除它”)。
我意识到这可能不是完整的代码,并且可能有理由为什么你这样设计它,但我个人建议更改界面以返回找到的子而不是使用out参数。 (您仍然可以通过测试null来区分成功和失败。)
实际查找代码不仅变得更简单:
NodePtr GetChildByKeyName(/*In*/ const std::string& key)
{
auto itor = _childNodes.find(key);
if (itor != _childNodes.end())
{
SomeOtherMethod();
return itor->second;
}
return nullptr;
}
然后,您也可以重复使用指向心脏内容的指针:
NodePtr spNode;
....
spNode = spNode->GetChildItem(strChildKeyName);
if (spNode)
spNode = spNode->GetChildItem(strSubKeyName);
答案 1 :(得分:3)
+1到@Dentoid's answer。我不打算在这里复制他的答案。我只会显示您现有的代码是否存在问题。
shared_ptr的分配是否可以删除
this
指针?
是的,确实如此。
我做了一个测试来确定它:http://coliru.stacked-crooked.com/a/ef0d4f92902b4dee
其输出(为清晰起见而格式化):
spNode before: 0x15a1028
this: 0x15a1028 <---------------------------------------------------------|
spChild.get() == this |
|
spChild before: 0x15a1028 <-------- Notice: They're the same -------------|
Node 0x15a1028 destroyed. |
^---------------------------------------------------------------|
spChild after: 0x15a1078 |
|
SomeOtherMethod() @0x15a1028; size: 1 | |
spNode after: 0x15a1078 ^------------------------------------------------|
Node 0x15a1078 destroyed.
所以在已经销毁的对象上调用SomeOtherMethod()
,尽管运行时无法检测到它。
应用您的解决方法,http://coliru.stacked-crooked.com/a/f0042d4b46fed340
spNode before: 0x19a7028
this: 0x19a7028
spChild.get() == this
spChild before: 0x19a7028
spChild after: 0x19a7078
SomeOtherMethod() @0x19a7028; size: 1
Node 0x19a7028 destroyed.
spNode after: 0x19a7078
Node 0x19a7078 destroyed.
它不再有问题了。