让我们来看一个表示子节点树的数据结构(节点)的以下示例。每个对象的子节点集存储在map>中

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

有什么想法吗?

最佳答案

您是正确的,如果第二个示例中最外面的spNode指针是对根项的唯一引用,则GetChildByKeyName将替换该引用,从而导致对象被破坏(实质上是“删除此对象”)。

我意识到这可能不是完整的代码,并且可能有您设计这样的原因,但是我个人建议更改接口(interface)以返回找到的子代,而不要使用out参数。 (您仍然可以通过测试null来区分找到 child 的成功与失败。)

实际的查找代码不仅变得更简单:

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);

09-25 19:13