Может ли назначение общего ptr мусорить указатель " this


Рассмотрим следующий пример структуры данных (узла), представляющей собой дерево дочерних узлов. Набор дочерних узлов для каждого объекта хранится в карте>

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 вызывающего объекта) разрушает "этот" узел, так как последняя ссылка только что ушла? (И, следовательно, вызов других методов после назначения spChild опасен). Есть ли у меня потенциальная ошибка здесь?

Я думаю, что обходной путь состоит в том, чтобы просто добавить эту строку в верхней части метода вызов:

NodePtr spChildRef = spChild; // maintain a reference to the caller's original node during the scope of the method

Мысли?

2 5

2 ответа:

Вы правы, что если самый внешний указатель spNode во втором примере является единственной ссылкой на корневой элемент, GetChildByKeyName заменит эту ссылку, вызывая разрушение объекта (по сути "удалить это").

Я понимаю, что это может быть не полный код, и могут быть причины, по которым вы его так спроектировали, но я лично предложил бы изменить интерфейс, чтобы вернуть найденного ребенка вместо использования параметра out. (Вы все еще можете различать между успех и неудача в поиске ребенка путем тестирования на ноль.)

Не только фактический код поиска становится проще:

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 к @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.

У него больше нет проблем.