Являются ли структуры данных подходящим местом для shared_ptr?

Я занимаюсь реализацией бинарного дерева на С++. Традиционно у меня был бы указатель влево и указатель вправо, но ручное управление памятью обычно заканчивается плачевно. Что приводит меня к моему вопросу...

Являются ли структуры данных подходящим местом для использования shared_ptr?


person Gabriel Isenberg    schedule 17.12.2008    source источник


Ответы (7)


Я думаю, это зависит от того, где вы будете их использовать. Я предполагаю, что вы думаете сделать что-то вроде этого:

template <class T>
class BinaryTreeNode 
{
    //public interface ignored for this example
    private:
        shared_ptr<BinaryTreeNode<T> > left;
        shared_ptr<BinaryTreeNode<T> > right;
        T data;
}

Это имело бы смысл, если вы ожидаете, что ваша структура данных будет обрабатывать динамически создаваемые узлы. Однако, поскольку это не нормальный дизайн, я думаю, что это неуместно.

Я бы ответил, что нет, это неподходящее место для использования shared_ptr, так как использование shared_ptr подразумевает, что объект на самом деле является общим, однако узел в двоичном дереве не никогда не используется совместно. Однако, как заметил Мартин Йорк, зачем изобретать велосипед, ведь уже есть умный тип указателя, который делает то, что мы пытаемся сделать, — auto_ptr. Итак, сделайте что-то вроде этого:

template <class T>
class BinaryTreeNode 
{
    //public interface ignored for this example
    private:
        auto_ptr<BinaryTreeNode<T> > left;
        auto_ptr<BinaryTreeNode<T> > right;
        T data;
}

Если кто-то спросит, почему данные не являются shared_ptr, ответ прост: если копии данных подходят для клиента библиотеки, они передаются в элементе данных, а узел дерева делает копировать. Если клиент решит, что копировать — плохая идея, тогда код клиента может быть передан в shared_ptr, который узел дерева может безопасно скопировать.

person Harper Shelby    schedule 17.12.2008
comment
Почему не auto_ptr‹›? Публичный интерфейс, который вы упустили. Теперь неизбежно придется включать код, который выполняет некоторое управление памятью. Вот почему у нас есть интеллектуальные указатели, поэтому мы не изобретаем велосипед при управлении памятью. - person Martin York; 18.12.2008
comment
Хорошо, auto_ptr будет для них допустимым вариантом — на самом деле, может быть даже лучше, поскольку его семантика согласуется с семантикой левого и правого указателей узла бинарного дерева. - person Harper Shelby; 18.12.2008
comment
Возможно, вы захотите распространить тип T на ваши объявления левого и правого, иначе не будет компилироваться. - person Mark Kegel; 18.12.2008

Поскольку левое и правое не являются общими, boost::shared_ptr‹>, вероятно, не является правильным умным указателем.

Здесь было бы неплохо попробовать std::auto_ptr‹>

person Martin York    schedule 17.12.2008

Да, конечно.

Но будьте осторожны, если у вас круговая структура данных. Если у вас есть два объекта, оба с общим ptr друг к другу, то они никогда не будут освобождены без ручной очистки общего ptr. В этом случае можно использовать слабый ptr. Это, конечно, не проблема с бинарным деревом.

person David Norman    schedule 17.12.2008
comment
дерево по своей природе не круглое. - person Martin York; 18.12.2008

Написать управление памятью вручную не так сложно в тех счастливых случаях, когда у каждого объекта есть единственный владелец, который, следовательно, может удалить то, что ему принадлежит, в своем деструкторе.

Учитывая, что дерево по определению состоит из узлов, каждый из которых имеет одного родителя и, следовательно, является очевидным кандидатом на роль единственного владельца, это как раз такой счастливый случай. Поздравляем!

Я думаю, что было бы неплохо * разработать такое решение для вашего случая, А ТАКЖЕ попробовать подход shared_ptr, полностью скрывающий различия за идентичным интерфейсом, поэтому вы переключаетесь между ними и сравниваете разницу в производительности с некоторыми реалистичными экспериментами. Это единственный верный способ узнать, подходит ли shared_ptr для вашего приложения.

(* для нас, если вы расскажете нам, как это происходит.)

person Daniel Earwicker    schedule 17.12.2008

Никогда не используйте shared_ptr для узлов структуры данных. Это может привести к приостановке или задержке уничтожения узла, если в какой-то момент право собственности было передано. Это может привести к тому, что деструкторы будут вызываться в неправильной последовательности. В структурах данных рекомендуется, чтобы конструкторы узлов содержали любой код, который связывается с другими узлами, а деструкторы содержали код, который отделяется от других узлов. Деструкторы, вызываемые в неправильной последовательности, могут нарушить эту схему.

person John Morrison    schedule 07.08.2009

Есть немного дополнительных накладных расходов с shared_ptr, особенно в требованиях к пространству, но если ваши элементы выделены индивидуально, тогда shared_ptr был бы идеальным.

person Mark Ransom    schedule 17.12.2008

Вам вообще нужны указатели? Кажется, вы могли бы использовать boost::optional<BinaryTreeNode<T> > left, right.

person MSalters    schedule 19.12.2008