2015-08-17 3 views
4

Когда следует использовать shared_ptr и when unique_ptr?Когда shared_ptr, when unique_ptr

Например, в этом классе вместо узла * должен быть shared_ptr или unique_ptr. От чего это зависит?

class node 
{ 
private: 
    node *parent; 
    vector<node*> children; 

    /* 
    * txny 
    * x - numer drzewa 
    * y - numer wezla 
    */ 
    string id; 
    typeNode type; //0 - term, 1 - func 

public: 
    node(node* parent, string id, typeNode type); 
    virtual ~node() {} 

    void addChild(node* child); 
    void setChildren(vector<node*> &children); 
    node* getChild(int i); 
    int getChildrenNumber(); 
    void setParent(node* parent); 
    node* getParent(); 
    string getId(); 
    typeNode getType(); 
    void setId(string id); 
}; 

РЕДАКТИРОВАТЬ:

дерево Класс владеет объект узла. Я должен написать больше текста, потому что не могу сохранить изменения.

class tree 
{ 
private: 
    vector <node*> nodes; 
    int depth; 
    int counterNodes; 
    /* 
    * pxty 
    * x - numer populacji (generacji) 
    * y - numer drzewa 
    */ 
    string id; 
private: 
    node* root; 
    node* parentGeneticPoint; 
public: 
    tree(int depth, string id); 
    tree(int depth); 
    ~tree(); 
public: 
    void initialize(typeInit type); 
    void showVector(); 
    void show(); 
    Mat run(); 
    tree* copy(); 
    tree* copySubtree(int subrootI); 
    tree* copyWithoutSubtree(int subrootI); 

};

+1

Кому принадлежат объекты 'node'? Семантика собственности обычно определяет тип используемого указателя. –

+0

Я редактировал. Дереву принадлежит это – user3191398

ответ

3

shared_ptr решает проблемы управления памятью, как регулярные выражения решают проблемы синтаксического анализа html.

shared_ptr может быть частью решения проблемы управления жизненным циклом, но это никоим образом не является формой или формой, которую можно использовать случайно.Очень легко иметь «неуместные» указатели или циклы ссылок с shared_ptr. По моему опыту, используйте shared_ptr как внутреннюю частную деталь реализации с охранниками, инвариантами и аксиомами, которые вместе доказывают, что вы не можете сформировать циклы, и у вас есть неплохие шансы на отсутствие проблем.

Более половины моего использования shared_ptr состоит из одного места, которое «владеет» указателем, и других наблюдателей, у которых есть weak_ptr, за исключением узких окон, когда они проверяют, что ресурс все еще существует, а также причина полагать, что shared_ptr не умрет в этом узком окне.

Еще один хороший способ использования - когда у меня ситуация с копированием на запись, где у меня есть состояние неизменного объекта, которое можно скопировать (хранится в shared_ptr<const T> pimpl. Когда происходит операция записи, если я ' м только один пользователя я бросил, что к shared_ptr<T> и изменить его. в противном случае, я копирую его в shared_ptr<T> и изменить его. Затем я храню его обратно как shared_ptr<const T> в обеих случаях.

Просто рассеяние shared_ptr s вокруг в моем опыте неизбежно приводит к утечкам и ресурсам, продолжающимся намного дольше, чем они должны.


С другой стороны, вы должны просто использовать unique_ptr. make_unique и unique_ptr должны заменить new и delete в вашем коде практически при любых обстоятельствах. На самом деле очень сложно получить unique_ptr, и, когда вы это делаете, обычно это связано с тем, что у старого кода был серьезный риск утечки или вы не понимали, каким образом этот ресурс управлялся раньше.

В новом коде это без проблем. В старом кодексе, если вам нужно понять время жизни объектов, вам нужно будет учиться достаточно, чтобы в полной мере разместить право собственности на unique_ptr. Большим исключением является то, что вы выполняете программирование «культового груза» (модифицируя сложную систему, которую вы не понимаете способами, похожими на другой код в системе, и надеетесь, что она будет работать, потому что другой код работал) не является возможно управлять таким ресурсом. Существуют также некоторые другие исключения, такие как объекты, которые управляют своей собственной жизнью сложным способом.

Управление ресурсами, не связанными с new, с unique_ptr является более сложным, но я также считаю, что стоит.

И иногда вы вынуждены .release указатель на длинный C-стиль void* заполненный звонок.


Существует также время выполнения накладных расходы на shared_ptr, но концептуальные накладные расходы shared_ptr делает объект жизнь гораздо труднее понять, реальная причина, чтобы избежать его использования.

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

На имеется почти нулевой промежуток времени выполнения: он имеет тот же размер, что и указатель, и он просто вызывает delete в конце жизненного цикла.

unique_ptr решает проблемы, связанные с управлением ресурсами, мертвыми на их пути. Они испаряются после правильного использования.


В вашем конкретном примере, я бы либо поместить unique_ptr с в tree, и сохранить сырые указатели в узлах.

В качестве альтернативы, сохраните unique_ptr с в векторе children и необработанных указателях в дереве и в указателе родителя.

В любом случае, все операции, которые добавляют/удалять узлы должны пройти через tree (принимая аргумент для узла мишени), как состояние tree и в node, необходимые, чтобы быть в синхронизации.


Вы выразили заинтересованность в получении случайный узел, когда я указал, что список узлов в корне tree была плохая идея.

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

Добавить:

node* node::nth_node(int n) { 
    if (n == 0) return this; 
    --n; 
    for(auto&& child:children) { 
    if (n < child->subtree_size) 
     return child->nth_node(n); 
    n -= child->subtree_size; 
    } 
    return nullptr; // n is too big 
} 

это становится п-й потомка узла, предполагая subtree_size, насколько велика дерево node является корнем (в том числе и себя, поэтому он никогда не должен быть 0).

Чтобы получить случайный узел из tree, создайте случайное число от 0 до root->subtree_size. Т.е., если root->subtree_size составляет 3, ваше случайное число: 0, 1 или 2.

Затем позвоните по номеру root->nth_node(that_random_number).

+0

Как и в моем коде. У меня есть в члене узла класса 'vector children' и в дереве классов' vector nodes'. В «узлах» я сохраняю все указатели на узел в моей древовидной структуре. В одном из этих узлов указатели store 'children' указывают на некоторые из этих узлов. Так не должно быть 'shared_ptr'? Или 'vector > children' и' vector nodes'? – user3191398

+1

Я предполагаю, что ваши родители владеют вашими детьми. Поэтому родитель должен иметь вектор уникальных указателей на дочерние элементы ...Указатель childs-to-parent может быть исключен или может быть необработанным указателем (не владеющим), что может быть только недействительным во время разрушения ребенка. С другой стороны, если вы хотите, чтобы дерево владели узлами, тогда у него должен быть вектор 'unique_ptr', а указатели в самих узлах дерева должны быть необработанными указателями. Любой из них хорош. – Yakk

+0

Это должно быть 'vector children' в узле класса и' vector > узлы в дереве классов. Потому что, если он был обратно, и я удаляю один из узлов в «узлах», он также будет удалять все дочерние элементы. Я прав? – user3191398

2

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

6

В случае с этой древовидной структурой у вас должно быть shared_ptr детям и weak_ptr для родителей.

Почему?

  • shared_ptr позволяет другим объектам также указывать на детей (например, если вы держите где-нибудь указатель на поддерево).
  • weak_ptr аналогичен shared_ptr, но не требует владения. Итак, если узел больше не указывает его родительский или другой shared_ptr, он будет уничтожен. Если у его собственных детей будет shared_ptr, объект никогда не будет уничтожен из-за ссылки cirular.

unique_ptr должен использоваться, если за указатель и его удаление несет ответственность только один объект. Это означает, в частности, что вы не должны делать копии указателя. Однако вы можете передать свою собственность.

Edit: дополнительная информация о

Комментарии показывают, что этот вопрос является более сложным, чем этот ответ. В конце концов, есть полные главы книги, посвященные этой теме ;-) Три вопроса помогут вам выбрать правильный дизайн: кому принадлежит объект, на который указывает? Указывать ли указатели на внешнее слово? Какие гарантии вы хотите дать об этих указателях?

Если вы хотите, чтобы другие объекты (т. Е. Вне структуры узла) безопасно указывали на ваши узлы (т. Е. Вы не хотите, чтобы узел исчезал во время использования снаружи), вам понадобится shared_ptr. Узел будет удален, если shared_ptr больше не ссылается на него.

Если, напротив, узел «владеет» своими детьми и несет ответственность за их уничтожение, возможно, это может пойти unique_ptr. unique_ptr::get() можно использовать для получения (необработанных) указателей на узлы, но без гарантии того, что они останутся действительными в более позднее время.

+4

Если родительский узел владеет дочерним, 'unique_ptr' является правильным выбором, а не' shared_ptr'. Вы можете поделиться не принадлежащим ему указателем на поддерево 'unique_ptr', используя' std :: unique_ptr :: get() '. «unique_ptr следует использовать, если только один объект должен использовать указатель» неверно. 'unique_ptr' следует использовать, если только один объект должен * иметь * указатель. Собственность и использование очень разные. –

+0

Это имеет смысл. Благодарю. Если у меня был другой класс, который содержит вектор , и я использую unique_ptr, могу ли я иметь такие функции, как void addTree (unique_ptr ) и unique_ptr getTree()? – user3191398

+0

@ AnthonyVallée-Dubois Я не думаю, что это тоже должно быть упрощено. 'unique_ptr :: get()' возвращает необработанный указатель, поэтому ваш узел может быть удален, даже если указатель поддерева все равно может понадобиться. Реальный вопрос - какие указатели вы намерены отдавать внешнему миру, какие гарантии. Я отредактирую, чтобы уточнить. – Christophe

Смежные вопросы