Используя умные указатели в C++ бинарный поиск в дереве

я реализовал двоичное дерево поиска в c++. Вместо того, чтобы использовать голые указатели для указания на детей узлов, я использовал std::shared_ptr. Узлы дерева реализованы следующим образом

struct treeNode;
typedef std::shared_ptr<treeNode> pTreeNode;

struct treeNode {
    T key;
    pTreeNode left;
    pTreeNode right;
    treeNode(T key) : key(key), left(nullptr), right(nullptr) {}
};

при удалении узла из BST, один из случаев, когда узел имеет только один дочерний. Узел просто заменяется этим дочерним элементом, как показано ниже:

             |        remove node                  
            node      --------->     |
                                   right 
              right                

в аналогичной реализации Java это может быть закодировано как:

node = node.getRight();

в моем c++ реализация это:

node = node->right;

где узел имеет тип pTreeNode.

при вызове оператора = на pTreeNode (std::shared_ptr<TreeNode>),'ы будет называться. Количество общих указателей, указывающих на базовый TreeNode равно 1, следовательно,TreeNode разрушается, освобождая свою память. Когда TreeNode (по умолчанию) вызывается деструктор, каждый из его членов уничтожается. Это, безусловно, приведет к pTreeNode right член разрушается. Проблема в том, что node->right это то, что назначается node. При тестировании моего BST он работает нормально без ошибок / утечек памяти.

  • то, что я делаю, небезопасно?
  • если это небезопасно, что я мог сделать, чтобы обойти эту проблему?

"хак", который я полагал, может работать, будет сделать еще один указатель, чтобы увеличить его количество ссылок. Будет ли это адекватным решением?

//increase reference to node->right by 1 so it doesn't get destroyed
pTreeNode temp(node->right);
node = node->right;

2 ответов


вы, по-видимому, предполагаете, что в

node = right;

shared_ptrоператор присваивания может уменьшить количество узлов до завершения чтения из right (или перед увеличением количества ссылок, используемых right). Однако, согласно cppreference, используя

template<typename T>
template<typename U>
std::shared_ptr<T> &std::shared_ptr<T>::operator =(const std::shared_ptr<U> &);

as

node = right; // std::shared_ptr<treeNode>

эквивалентно

std::shared_ptr<treeNode>(right).swap(node);

, который является безопасным, потому что right скопировать до старое значение node разрушен. Как кроме того, я сам реализовал общий указатель, и я следил за тем, чтобы" очистка " старого значения была последние что я делаю внутри operator = именно, чтобы избежать таких проблем.


  • то, что я делаю, небезопасно?

нет, насколько я вижу, это безопасно. The left или right экземпляры узлов будут сохраняться до тех пор, пока их количество ссылок не упадет до нуля.

  • если это небезопасно, что я мог сделать, чтобы обойти эту проблему?

единственное, что вы должны знать, это не раздавать какие-либо узлы как shared_ptr ' s вне реализации дерева. Они должны будь std::weak_ptrили сырые указатели.