5

让我们以表示子节点树的数据结构(节点)为例。每个对象的子节点集存储在地图中>

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 是对该对象的最后剩余引用,那么我担心 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

想法?

4

2 回答 2

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);
于 2014-03-05T07:20:53.457 回答
3

+1 @Dentoid 的回答。我不会在这里重复他的答案。我只会显示您现有的代码是否有问题。


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.

SoSomeOtherMethod()在已经销毁的对象上调用,尽管运行时无法检测到这一点。

应用您的解决方法,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.

它不再有问题了。

于 2014-03-05T07:25:54.193 回答