Re[7]: help: практика работы с shared_ptr
От: remark Россия http://www.1024cores.net/
Дата: 12.11.10 11:11
Оценка: 5 (1) +1
Здравствуйте, samius, Вы писали:

S>допустим, некий код построил дерево и отдал другому коду какую-то ветку (надолго, на хранение). Теперь мы обязаны заботиться что бы все дерево не было освобождено раньше чем перестанет быть нужна его ветка, отданная в другое место.


А, понятно, что ты делаешь.
Я бы наверное использовал интрузивные счётчики "в стиле COM", а проблему слабого указателя на родителя решил бы тем, что родитель обнуляет указатель на себя у всех дочерних узлов (у него все равно есть этот список). Что-то типа такого:

struct node_t
{
    unsigned refcount_;
    node_t* parent_;
    std::vector<node_t*> children_;
    int data_;

    node_t(int data)
    {
        data_ = data;
        refcount_ = 1;
        parent_ = 0;
    }

    node_t* acquire()
    {
        ++refcount_;
        return this;
    }

    void release()
    {
        if (--refcount_ == 0)
            delete this;
    }

    void add_child(node_t* n)
    {
        assert(n->parent_ == 0);
        n->acquire();
        n->parent_ = this;
        children_.push_back(n);
    }

    ~node_t()
    {
        assert(parent_ == 0);
        for (auto child = children_.begin(); child != children_.end(); ++child)
        {
            assert((*child)->parent_ == this);
            (*child)->parent_ = 0;
            (*child)->release();
        }
    }
};

int main()
{
    node_t* n1 = new node_t (1);
    node_t* n2 = new node_t (2);
    node_t* n3 = new node_t (3);
    // собираем дерево
    n1->add_child(n2);
    n2->release();
    n1->add_child(n3);
    n3->release();

    // захватываем один из подузлов
    node_t* n4 = n2->acquire();

    // удаляем дерево, но наш узел пока живёт
    n1->release();

    // отпускаем наш подузел
    n4->release();
}



1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[3]: help: практика работы с shared_ptr
От: remark Россия http://www.1024cores.net/
Дата: 12.11.10 09:23
Оценка: +2
Здравствуйте, samius, Вы писали:

R>>KISS. Когда изымаешь узел из дерева, удаляешь его. Всё.


S>Помедитирую над этим. С дотнетом головного мозга тянет моделировать GC, так как нет привычки следить за владением объектами. Сейчас лихорадочно пытаюсь прикинуть, будет ли нужно шарить поддеревья, и если да, то кто будет владеть узлами.


Их в принципе не получится шарить — parent и набор дочерних узлов один. Назначение этого узла в том и есть, что бы быть одним узлом одного дерева. Поэтому создал, добавил, изъял, удалил. Всё очень просто.
Пользовательские же данные — это другое дело. Пользователь может захотеть положить один объект в несколько деревьев. Но это пусть он думает, что передать в качестве T, может int, а может shared_ptr<vector<string>>.


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re: help: практика работы с shared_ptr
От: ononim  
Дата: 12.11.10 13:53
Оценка: +2
S>Однако, есть ньюансы, связанные со спецификой shared_ptr, а точнее внешним хранением блока счетчиков ссылок по отношению к объекту. Нельзя создавать более одного shared_ptr по сырому указателю Node*.

S>Хотелось бы узнать, как принято работать с shared_ptr в таких случаях?

intrusive_ptr — и экономнее и проблему данную решает..
Как много веселых ребят, и все делают велосипед...
Re[3]: help: практика работы с shared_ptr
От: uzhas Ниоткуда  
Дата: 12.11.10 10:59
Оценка: 5 (1)
Здравствуйте, samius, Вы писали:

S>Разве удаление объекта не нарушает его константность?

тут дело в не в смарт-пойнтерах. а в языке
удалять объект разрешено, если есть указатель на конст объект
const A* const ptr = new A();
delete ptr;

S>А сконвертировать shared_ptr<MyClass> в shard_ptr<const MyClass>?
сконвертить можно простым присваиванием вроде
можно и убирать конст (аналог const_cast для shared_ptr)
http://www.boost.org/doc/libs/1_41_0/libs/smart_ptr/shared_ptr.htm#const%5Fpointer%5Fcast
Re: help: практика работы с shared_ptr
От: c-smile Канада http://terrainformatica.com
Дата: 13.11.10 18:55
Оценка: 5 (1)
Здравствуйте, samius, Вы писали:

S>Допустим, требуется организовать дерево с указателями на родителей. shared_ptr<T> — то что доктор прописал по этому поводу:

S>
S>class Node
S>{
S>    vector< shared_ptr<Node> > _children;
S>    weak_ptr<Node> _parent;
S>    ...
S>};
S>


В htmlayout/sciter дерево DOM объектов устроено таким образом:

class node: public ref_counted
{
  node* parent;
  collection< handle<node> > children;
};


Где handle<node> это smart ptr — делает ref_counted::add_ref/release при установке/очистке.
Делал это все руками ибо не нашел в stl в свое время подходящей идиомы которая позволяла бы это делать с той же эффективностью.
Re[5]: help: практика работы с shared_ptr
От: Erop Россия  
Дата: 12.11.10 09:53
Оценка: 1 (1)
Здравствуйте, samius, Вы писали:

R>>Их в принципе не получится шарить — parent и набор дочерних узлов один. Назначение этого узла в том и есть, что бы быть одним узлом одного дерева. Поэтому создал, добавил, изъял, удалил. Всё очень просто.

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

А это и не надо.
Вполне достаточно, чтобы родитель, в своём деструкторе, попросил детей забыть о себе...

Я бы, либо, заюзал intrisive_ptr, либо свой. Для хранения ссылки на ноду. Как из отца, так и из клиентского кода.
И просто голый указатель, для хранения в ноде указателя на родителя.
+ Когда ноду усыновляют, в неё прописывают родителя, а когда родитель умирает, он отписывается от своих нод, и делает им Release, ну, в смысле, разрушает свои коппии intrisive_ptr...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[8]: help: практика работы с shared_ptr
От: sokel Россия  
Дата: 12.11.10 15:20
Оценка: 1 (1)
Здравствуйте, remark, Вы писали:

R>Я бы наверное использовал интрузивные счётчики "в стиле COM", а проблему слабого указателя на родителя решил бы тем, что родитель обнуляет указатель на себя у всех дочерних узлов (у него все равно есть этот список). Что-то типа такого:

...

только вектор тут ни к чему, я бы как нибудь так сделал:

struct node_t
{
    node_t* parent_;
    node_t* next_;
    node_t* children_;


ну или так:

struct node_t : intrusive_list_member<> // или какой нибудь intrusive_set если надо по детям поиск прикручивать
{
    node_t* parent_;
    intrusive_list<node_t> children_;
Re[13]: help: практика работы с shared_ptr
От: Erop Россия  
Дата: 14.11.10 13:18
Оценка: 1 (1)
Здравствуйте, samius, Вы писали:


S>Т.е. пока мне чихать на эффективность до той степени что я могу себе позволить вектора shared_ptr. Стоит ли (принято ли) в таких случаях заморачиваться упрятыванием указателей T* от греха подальше? Либо все лечится дисциплиной и передачей исключительно shared_ptr<T> по ссылке либо по значению?


Ну вообще так строят работу, что голые указатели нигде и никогда не фигурируют.
Но в твоём случае логичнее и безопаснее юзать intrusive_ptr.

S>Например в гвайде гугла рекомендуют воздерживаться от shared_ptr (но по другим причинам).

Ну мне он тоже не нравится.
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re: help: практика работы с shared_ptr
От: Kluev  
Дата: 12.11.10 08:54
Оценка: -1
Здравствуйте, samius, Вы писали:

S>Доброе время суток!

S>Помогите, пожалуйста, определиться:

S>Допустим, требуется организовать дерево с указателями на родителей. shared_ptr<T> — то что доктор прописал по этому поводу:

S>
S>class Node
S>{
S>    vector< shared_ptr<Node> > _children;
S>    weak_ptr<Node> _parent;
S>    ...
S>};
S>


Зачем вообще нужен этот огород? Используешь простые указатели и удаляешь узлы в деструкторе. Или еще лучше написать обертку вокруг вектора которая удаляет содержимое в своем деструкторе.
vector< shared_ptr<Node> > — фейлдизайн.
Re[2]: help: практика работы с shared_ptr
От: AndrewJD США  
Дата: 12.11.10 09:02
Оценка: +1
Здравствуйте, Kluev, Вы писали:

K>vector< shared_ptr<Node> > — фейлдизайн.

Спорное утверждение
"For every complex problem, there is a solution that is simple, neat,
and wrong."
Re[2]: help: практика работы с shared_ptr
От: samius Япония http://sams-tricks.blogspot.com
Дата: 12.11.10 09:08
Оценка: :)
Здравствуйте, remark, Вы писали:

R>KISS. Когда изымаешь узел из дерева, удаляешь его. Всё.


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

R>

Re[3]: help: практика работы с shared_ptr
От: remark Россия http://www.1024cores.net/
Дата: 12.11.10 09:25
Оценка: +1
Здравствуйте, samius, Вы писали:

K>>Зачем вообще нужен этот огород? Используешь простые указатели и удаляешь узлы в деструкторе. Или еще лучше написать обертку вокруг вектора которая удаляет содержимое в своем деструкторе.

S>Как уже ответил выше — дотнет ГМ. Что интересно — нет желания приучаться следить за владением объектов.

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


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[11]: help: практика работы с shared_ptr
От: remark Россия http://www.1024cores.net/
Дата: 13.11.10 08:02
Оценка: +1
Здравствуйте, samius, Вы писали:

S>>>только вектор тут ни к чему, я бы как нибудь так сделал:


R>>Согласен, так ещё лучше.


S>А чем лучше?


S>Как-то странно. Вроде язык высокоуровневый считается, куча умных контейнеров и указателей в библиотеках.. А как доходит до банального дерева — приходится выпиливать его руками с нуля


Основная сила С++ в том, что можно и так и так.
Если делаешь прототип или утилиту, то используешь std::vector, выделяешь всё по new и не удаляешь (получается настоящий GC).
А если тебе надо будет этим парсить 100-мегабайтный XML в DOM, то тут интрузивные линки, и region аллокатор огого как пригодятся.


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
help: практика работы с shared_ptr
От: samius Япония http://sams-tricks.blogspot.com
Дата: 12.11.10 08:32
Оценка:
Доброе время суток!
Помогите, пожалуйста, определиться:

Допустим, требуется организовать дерево с указателями на родителей. shared_ptr<T> — то что доктор прописал по этому поводу:
class Node
{
    vector< shared_ptr<Node> > _children;
    weak_ptr<Node> _parent;
    ...
};


Однако, есть ньюансы, связанные со спецификой shared_ptr, а точнее внешним хранением блока счетчиков ссылок по отношению к объекту. Нельзя создавать более одного shared_ptr по сырому указателю Node*.

Хотелось бы узнать, как принято работать с shared_ptr в таких случаях?

Варианты, которые приходят в голову следующие:
1) следить за руками, что бы ненароком не написали сырой Node* и везде принимать/передавать shared_ptr<Node>

2) инкапсулировать указатели на хранимые в shared_ptr данные.. Например, так:
class Node
{
    struct NodeData
    {
        vector<Node> _children;
        weak_ptr<NodeData> _parent;
    };
    shared_ptr<NodeData> _data;
};


Здесь узел является оберткой над shared_ptr указателем на свои данные. Т.е. фактически сырые указатели спрятаны от использующего кода и при корректной реализации испортить извне что-то будет сложно. Однако, меняется семантика, полиморфизм сильно усложняется.


Вместе с тем доступны другие подходы по организации дерева. Например, интрузивный счетчик ссылок аки в COM, позволяет смешивать в разумных пределах работу с сырыми указателями и смартпоинтерами за счет того что счетчик жестко привязан к объекту. Однако время жизни счетчика и самого объекта связаны, потому невозможен подход со слабыми указателями, а значит либо циклические ссылки в дереве, где нужно знать родителя (если указатель на родителя умный), либо проблема устаревания указателя (если указатель глупый).
Еще один возможный подход — привязка блока счетчиков к указываемому объекту:
class RefCounted
{
    struct Counter
    {
        int strong_counter;
        int weak_counter;
        RefCounted* ptr;
    }
private:
    Counter *c;
}
template <class T> strong_ptr
{
    T* ptr; // указывает на сам объект
}
template <class T> weak_ptr
{
    RefCounted::Counter* ptr; // указывает на блок счетчиков
}
class Node: public RefCounted
{
    vector< strong_ptr<Node> > children;
    weak_ptr<Node> parent;
}

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

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

Меня интересует мнения практиков C++ по поводу вышеописанного. Как принято, как бы вы поступили в своем проекте?

И еще: верно ли я понимаю, что смартпоинтеры с управлением жизни объекта не совместимы с константностью?
Re: help: практика работы с shared_ptr
От: remark Россия http://www.1024cores.net/
Дата: 12.11.10 08:44
Оценка:
Здравствуйте, samius, Вы писали:

S>Допустим, требуется организовать дерево с указателями на родителей. shared_ptr<T> — то что доктор прописал по этому поводу:

S>
S>class Node
S>{
S>    vector< shared_ptr<Node> > _children;
S>    weak_ptr<Node> _parent;
S>    ...
S>};
S>


KISS. Когда изымаешь узел из дерева, удаляешь его. Всё.


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[2]: help: практика работы с shared_ptr
От: samius Япония http://sams-tricks.blogspot.com
Дата: 12.11.10 09:10
Оценка:
Здравствуйте, Kluev, Вы писали:

K>Здравствуйте, samius, Вы писали:


S>>Доброе время суток!

S>>Помогите, пожалуйста, определиться:

K>Зачем вообще нужен этот огород? Используешь простые указатели и удаляешь узлы в деструкторе. Или еще лучше написать обертку вокруг вектора которая удаляет содержимое в своем деструкторе.

Как уже ответил выше — дотнет ГМ. Что интересно — нет желания приучаться следить за владением объектов.

K>vector< shared_ptr<Node> > — фейлдизайн.

можно развернуть тезис?
Re[4]: help: практика работы с shared_ptr
От: samius Япония http://sams-tricks.blogspot.com
Дата: 12.11.10 09:35
Оценка:
Здравствуйте, remark, Вы писали:

R>Здравствуйте, samius, Вы писали:


R>>>KISS. Когда изымаешь узел из дерева, удаляешь его. Всё.


S>>Помедитирую над этим. С дотнетом головного мозга тянет моделировать GC, так как нет привычки следить за владением объектами. Сейчас лихорадочно пытаюсь прикинуть, будет ли нужно шарить поддеревья, и если да, то кто будет владеть узлами.


R>Их в принципе не получится шарить — parent и набор дочерних узлов один. Назначение этого узла в том и есть, что бы быть одним узлом одного дерева. Поэтому создал, добавил, изъял, удалил. Всё очень просто.

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

R>Пользовательские же данные — это другое дело. Пользователь может захотеть положить один объект в несколько деревьев. Но это пусть он думает, что передать в качестве T, может int, а может shared_ptr<vector<string>>.


согласен, но я не уточнил, что речь идет не об обобщенном Node<T>, а о конкретном, например XmlNode.
R>
Re[2]: help: практика работы с shared_ptr
От: samius Япония http://sams-tricks.blogspot.com
Дата: 12.11.10 09:47
Оценка:
Здравствуйте, Kluev, Вы писали:

K>Зачем вообще нужен этот огород? Используешь простые указатели и удаляешь узлы в деструкторе. Или еще лучше написать обертку вокруг вектора которая удаляет содержимое в своем деструкторе.

K>vector< shared_ptr<Node> > — фейлдизайн.

а что по поводу vector< scoped_ptr<Node> >?
Вобще зачем удалять что-то в деструкторе, если можно скомбинировать типы и не удалять что-то в деструкторе? В конце концов C++ а не C.
Re[3]: help: практика работы с shared_ptr
От: Erop Россия  
Дата: 12.11.10 09:49
Оценка:
Здравствуйте, samius, Вы писали:

S>Помедитирую над этим. С дотнетом головного мозга тянет моделировать GC, так как нет привычки следить за владением объектами. Сейчас лихорадочно пытаюсь прикинуть, будет ли нужно шарить поддеревья, и если да, то кто будет владеть узлами.


Если ты планируешь шарить поддеревья, то подумай о том, указатель на кого их родителей ты хочешь иметь в поддереве?
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[4]: help: практика работы с shared_ptr
От: samius Япония http://sams-tricks.blogspot.com
Дата: 12.11.10 09:52
Оценка:
Здравствуйте, Erop, Вы писали:

E>Здравствуйте, samius, Вы писали:


S>>Помедитирую над этим. С дотнетом головного мозга тянет моделировать GC, так как нет привычки следить за владением объектами. Сейчас лихорадочно пытаюсь прикинуть, будет ли нужно шарить поддеревья, и если да, то кто будет владеть узлами.


E>Если ты планируешь шарить поддеревья, то подумай о том, указатель на кого их родителей ты хочешь иметь в поддереве?


речь была о том чтобы шарить ответственность за освобожденние, а не узлы между деревьями. Выше уже поправился.
Re[5]: help: практика работы с shared_ptr
От: remark Россия http://www.1024cores.net/
Дата: 12.11.10 10:02
Оценка:
Здравствуйте, samius, Вы писали:

S>>>Помедитирую над этим. С дотнетом головного мозга тянет моделировать GC, так как нет привычки следить за владением объектами. Сейчас лихорадочно пытаюсь прикинуть, будет ли нужно шарить поддеревья, и если да, то кто будет владеть узлами.


E>>Если ты планируешь шарить поддеревья, то подумай о том, указатель на кого их родителей ты хочешь иметь в поддереве?


S>речь была о том чтобы шарить ответственность за освобожденние, а не узлы между деревьями. Выше уже поправился.


А чего тут шарить ответственность? В конец remove() добавляешь free(), кто вызвал remove(), тот вызовет и free(). О каком разделении ответственности идёт речь?


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[6]: help: практика работы с shared_ptr
От: samius Япония http://sams-tricks.blogspot.com
Дата: 12.11.10 10:06
Оценка:
Здравствуйте, remark, Вы писали:

R>Здравствуйте, samius, Вы писали:


S>>речь была о том чтобы шарить ответственность за освобожденние, а не узлы между деревьями. Выше уже поправился.


R>А чего тут шарить ответственность? В конец remove() добавляешь free(), кто вызвал remove(), тот вызовет и free(). О каком разделении ответственности идёт речь?


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

R>
Re[3]: help: практика работы с shared_ptr
От: samius Япония http://sams-tricks.blogspot.com
Дата: 12.11.10 10:15
Оценка:
Здравствуйте, samius, Вы писали:

S>Здравствуйте, Kluev, Вы писали:


S>а что по поводу vector< scoped_ptr<Node> >?


прошу прощения, тут я сам лопухнул. scoped_ptr есть nocopyable, в вектор ему не пролезть.
Re[7]: help: практика работы с shared_ptr
От: Erop Россия  
Дата: 12.11.10 10:20
Оценка:
Здравствуйте, samius, Вы писали:

S>допустим, некий код построил дерево и отдал другому коду какую-то ветку (надолго, на хранение). Теперь мы обязаны заботиться что бы все дерево не было освобождено раньше чем перестанет быть нужна его ветка, отданная в другое место.


А ты хочешь, чтобы так было, или ты хочешь, чтобы так не было?
Потому, как хранить можно только подветку, вынув её из дерева, и забыв о родителях.

А ещё можно фейкового родителя иметь, кстати.
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[8]: help: практика работы с shared_ptr
От: samius Япония http://sams-tricks.blogspot.com
Дата: 12.11.10 10:24
Оценка:
Здравствуйте, Erop, Вы писали:

E>Здравствуйте, samius, Вы писали:


S>>допустим, некий код построил дерево и отдал другому коду какую-то ветку (надолго, на хранение). Теперь мы обязаны заботиться что бы все дерево не было освобождено раньше чем перестанет быть нужна его ветка, отданная в другое место.


E>А ты хочешь, чтобы так было, или ты хочешь, чтобы так не было?

Хочу что бы не пришлось следить. Только тут нужно разобраться, что вреднее, не следить или хотеть.

E>Потому, как хранить можно только подветку, вынув её из дерева, и забыв о родителях.

В случае shared_ptr можно хранить и дерево и ветки как в сборе, так и отдельно вполне прозрачно. В том и прелесть.
Re: help: практика работы с shared_ptr
От: uzhas Ниоткуда  
Дата: 12.11.10 10:28
Оценка:
Здравствуйте, samius, Вы писали:

S>Доброе время суток!

S>Помогите, пожалуйста, определиться:

S>Допустим, требуется организовать дерево с указателями на родителей. shared_ptr<T> — то что доктор прописал по этому поводу:

S>
S>class Node
S>{
S>    vector< shared_ptr<Node> > _children;
S>    weak_ptr<Node> _parent;
S>    ...
S>};
S>


S>Однако, есть ньюансы, связанные со спецификой shared_ptr, а точнее внешним хранением блока счетчиков ссылок по отношению к объекту. Нельзя создавать более одного shared_ptr по сырому указателю Node*.

если вы перешли на смарт-пойнтеры, то должны уйти от сырых указателей и этого нюанса не будет в принципе

S>Меня интересует мнения практиков C++ по поводу вышеописанного. Как принято, как бы вы поступили в своем проекте?

универсальных деревьев нет, нужно смотреть специфику задачи.
если цель сделать наиболее корректное и менее подверженное ошибкам при использовании, то полностью на смарт-пойнтерах (с ручным разбитием циклов weak_ptr в помощь) (нужно четко представлять кто имеет сильную ссылку, а кому дать слабую)
если важна производительность, то на сырых указателях с ручным выделением\освобождением ресурсов (главное сформулировать четкие правила кто и когда должен освобождать ресурс)
можно однонаправленное дерево сделать (ноды не знают своего родителя), чтобы облегчить себе жизнь. для обхода можно итераторы использовать

S>И еще: верно ли я понимаю, что смартпоинтеры с управлением жизни объекта не совместимы с константностью?

не очень понятен вопрос. shared_ptr<const MyClass> вполне можно делать)
Re[2]: help: практика работы с shared_ptr
От: samius Япония http://sams-tricks.blogspot.com
Дата: 12.11.10 10:39
Оценка:
Здравствуйте, uzhas, Вы писали:

U>Здравствуйте, samius, Вы писали:


S>>Меня интересует мнения практиков C++ по поводу вышеописанного. Как принято, как бы вы поступили в своем проекте?

U>универсальных деревьев нет, нужно смотреть специфику задачи.
нет, речь не об универсальном дереве. Узел есть нечто вроде узла дерева XML. Пока они однотипны, но сейчас еще не видно, будет ли специализация.

U>если цель сделать наиболее корректное и менее подверженное ошибкам при использовании, то полностью на смарт-пойнтерах (с ручным разбитием циклов weak_ptr в помощь) (нужно четко представлять кто имеет сильную ссылку, а кому дать слабую)

цель сделать пока удобное в использовании дерево. где втыкать слабую — я представляю.

U>если важна производительность, то на сырых указателях с ручным выделением\освобождением ресурсов (главное сформулировать четкие правила кто и когда должен освобождать ресурс)

На этом этапе хочется избежать ручной реализации освобождения.

U>можно однонаправленное дерево сделать (ноды не знают своего родителя), чтобы облегчить себе жизнь. для обхода можно итераторы использовать

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

S>>И еще: верно ли я понимаю, что смартпоинтеры с управлением жизни объекта не совместимы с константностью?

U>не очень понятен вопрос. shared_ptr<const MyClass> вполне можно делать)
Разве удаление объекта не нарушает его константность?
А сконвертировать shared_ptr<MyClass> в shard_ptr<const MyClass>?
Re[9]: help: практика работы с shared_ptr
От: Erop Россия  
Дата: 12.11.10 10:44
Оценка:
Здравствуйте, samius, Вы писали:

E>>Потому, как хранить можно только подветку, вынув её из дерева, и забыв о родителях.

S>В случае shared_ptr можно хранить и дерево и ветки как в сборе, так и отдельно вполне прозрачно. В том и прелесть.

На самом деле не всё так радужно. GC сделать трудно. Зато не нужно.
Можно пойти по другому пути. Можно сделать свой указатель на ноду, который будет хранить счётчик сразу на дерево. Но я подозреваю, что это не то, что нужно, на самом-то деле.

Обычно удобно, когда ветка дерева может жить обособлено. Но это уже от твоей задачи зависит, на самом деле.
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[10]: help: практика работы с shared_ptr
От: samius Япония http://sams-tricks.blogspot.com
Дата: 12.11.10 10:57
Оценка:
Здравствуйте, Erop, Вы писали:

E>Здравствуйте, samius, Вы писали:


E>>>Потому, как хранить можно только подветку, вынув её из дерева, и забыв о родителях.

S>>В случае shared_ptr можно хранить и дерево и ветки как в сборе, так и отдельно вполне прозрачно. В том и прелесть.

E>На самом деле не всё так радужно. GC сделать трудно. Зато не нужно.

E>Можно пойти по другому пути. Можно сделать свой указатель на ноду, который будет хранить счётчик сразу на дерево. Но я подозреваю, что это не то, что нужно, на самом-то деле.

E>Обычно удобно, когда ветка дерева может жить обособлено. Но это уже от твоей задачи зависит, на самом деле.


Задача — построение задачеориентированной объектной модели для обработки XML. В частности из XML дерево и получается, но потом обрастает спецификой задачи, из атрибутов вырастают свойства, появляеются спецметоды обработки и т.п.

Смущает меня то, что если мы добавляем к дереву А ветку Б, то получим дерево. Если мы от дерева отнимем ветку Б, то мы ее уже не сможем приклеить к дереву В, т.к. она будет тут же удалена. Как-то не хочется различать операции удаления поддерева от отцепления поддерева.
Хочется что бы отодрал ветку — она жива. Бросил — уничтожилась.
Re[3]: help: практика работы с shared_ptr
От: Alexey Sudachen Чили  
Дата: 12.11.10 11:01
Оценка:
S>нет, речь не об универсальном дереве. Узел есть нечто вроде узла дерева XML. Пока они однотипны, но сейчас еще не видно, будет ли специализация.
S>На этом этапе хочется избежать ручной реализации освобождения.

Ввести сущность контейнер-дерева, который содержит в себе узлы. Это дополнительно помогает оптимизировать память. Можно считать контейнер за пул узлов. Каждый узел имеет ссылку на контейнер (или можно вычислить адрес контейнера, если это линейный кусок памяти). Сам контейнер имеет счётчик ссылок и соответственно адресация к узлам идёт через указатель с подсчётом ссылок, в котором пользуется счётчик контейнера.
Re[4]: help: практика работы с shared_ptr
От: samius Япония http://sams-tricks.blogspot.com
Дата: 12.11.10 11:05
Оценка:
Здравствуйте, uzhas, Вы писали:

U>Здравствуйте, samius, Вы писали:


S>>Разве удаление объекта не нарушает его константность?

U>тут дело в не в смарт-пойнтерах. а в языке
U>удалять объект разрешено, если есть указатель на конст объект
U>
U>const A* const ptr = new A();
U>delete ptr;
U>

По меньшей мере странно это выглядит. Изменять нельзя, удалять можно.

S>>А сконвертировать shared_ptr<MyClass> в shard_ptr<const MyClass>?

U>сконвертить можно простым присваиванием вроде
U>можно и убирать конст (аналог const_cast для shared_ptr)
U>http://www.boost.org/doc/libs/1_41_0/libs/smart_ptr/shared_ptr.htm#const%5Fpointer%5Fcast
спасибо за ссылку, не натыкался на такой метод.
Re[11]: help: практика работы с shared_ptr
От: Erop Россия  
Дата: 12.11.10 11:10
Оценка:
Здравствуйте, samius, Вы писали:

S>Хочется что бы отодрал ветку — она жива. Бросил — уничтожилась.


Ну, то есть, ветки могут жить без дерева/без родителя? Я верно понял?

ну тогда делаешь просто.

1) На ноду в клиентском коде всегда указываешь через shared_ptr
2) Нода на своих детей тоже через shared_ptr указывает
3) Дети хранят в себе обычный указатель на родителя.
4) При добавлении подветки (в том числе и одинокой ноды) в дерево, в смысле в список детей какого-то родителя, родитель регится в своём новом ребёнке.
При отделении ребёнка в свободное плавание -- родитель разрегистрируется в ребёнке. И ребёнок становится сиротой.

то есть код метода AddChild будет какой-то такой:
void MyTree::AddChild( shared_ptr<MyTree> newChild )
{
    if( newChild->HasParent() ) {
        newChild->Detach();
    }
    children.push_back( newChild );
    newChild->setParent( this );
}


А в деструкторе MyTree каждому детю из children скажут setParent( 0 );\


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

Ну и последнее. Я не знаю насколько большие деревья предполагаются, но если реально большие, то меня бы на shared_ptr задавила бы жаба. Можно же раздельнео владение со счётчиком и подешевле организовать!
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[4]: help: практика работы с shared_ptr
От: samius Япония http://sams-tricks.blogspot.com
Дата: 12.11.10 11:11
Оценка:
Здравствуйте, Alexey Sudachen, Вы писали:

S>>нет, речь не об универсальном дереве. Узел есть нечто вроде узла дерева XML. Пока они однотипны, но сейчас еще не видно, будет ли специализация.

S>>На этом этапе хочется избежать ручной реализации освобождения.

AS>Ввести сущность контейнер-дерева, который содержит в себе узлы. Это дополнительно помогает оптимизировать память. Можно считать контейнер за пул узлов. Каждый узел имеет ссылку на контейнер (или можно вычислить адрес контейнера, если это линейный кусок памяти). Сам контейнер имеет счётчик ссылок и соответственно адресация к узлам идёт через указатель с подсчётом ссылок, в котором пользуется счётчик контейнера.


До знакомства с дотнетом мне бы такое решение понравилось
Re[12]: help: практика работы с shared_ptr
От: samius Япония http://sams-tricks.blogspot.com
Дата: 12.11.10 11:17
Оценка:
Здравствуйте, Erop, Вы писали:

E>Здравствуйте, samius, Вы писали:


S>>Хочется что бы отодрал ветку — она жива. Бросил — уничтожилась.


E>Ну, то есть, ветки могут жить без дерева/без родителя? Я верно понял?

да

E>ну тогда делаешь просто.


E>1) На ноду в клиентском коде всегда указываешь через shared_ptr

E>2) Нода на своих детей тоже через shared_ptr указывает
E>3) Дети хранят в себе обычный указатель на родителя.
E>4) При добавлении подветки (в том числе и одинокой ноды) в дерево, в смысле в список детей какого-то родителя, родитель регится в своём новом ребёнке.
E>При отделении ребёнка в свободное плавание -- родитель разрегистрируется в ребёнке. И ребёнок становится сиротой.
да, что-то подобное я подразумевал в стартовом посте.

E>то есть код метода AddChild будет какой-то такой:
void MyTree::AddChild( shared_ptr<MyTree> newChild )

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

E>Теперь то, что касается "исчезнет, как только я его оттуда достану". Если ты всё время будешь иметь shared_ptr на ноду, то она не исчезнет. Просто может осиротеть.

да, это я представляю.

E>Ну и последнее. Я не знаю насколько большие деревья предполагаются, но если реально большие, то меня бы на shared_ptr задавила бы жаба. Можно же раздельнео владение со счётчиком и подешевле организовать!

нет, реально не очень большие, жаба пока не давит.
Re[13]: help: практика работы с shared_ptr
От: Erop Россия  
Дата: 12.11.10 11:24
Оценка:
Здравствуйте, samius, Вы писали:

S>да

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

Тогда не ясно в чём твои проблемы-то?
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[8]: help: практика работы с shared_ptr
От: samius Япония http://sams-tricks.blogspot.com
Дата: 12.11.10 11:27
Оценка:
Здравствуйте, remark, Вы писали:

R>Здравствуйте, samius, Вы писали:


S>>допустим, некий код построил дерево и отдал другому коду какую-то ветку (надолго, на хранение). Теперь мы обязаны заботиться что бы все дерево не было освобождено раньше чем перестанет быть нужна его ветка, отданная в другое место.


R>А, понятно, что ты делаешь.

R>Я бы наверное использовал интрузивные счётчики "в стиле COM", а проблему слабого указателя на родителя решил бы тем, что родитель обнуляет указатель на себя у всех дочерних узлов (у него все равно есть этот список).
Да, к интрузивным счетчикам я уже пришел, но ввожу их через базовый класс и таки указатель на объекты с интрузивынми счетчиками сделал умными.

А вот решение проблемы слабого родителя через удаление ссылки родителем мне показалось интереснее, чем впендюривание weak_ptr в интрузивный подсчет ссылок.

R>Что-то типа такого:

+1, только по-ближе к C++, чем к C.

R>

Re[14]: help: практика работы с shared_ptr
От: samius Япония http://sams-tricks.blogspot.com
Дата: 12.11.10 11:31
Оценка:
Здравствуйте, Erop, Вы писали:

E>Здравствуйте, samius, Вы писали:


S>>да

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

E>Тогда не ясно в чём твои проблемы-то?


проблемы в потенциальной опасности нарушения целостности памяти путем случайного приведения shared_ptr<T> к T* и обратно. Дисциплина и порядок — это решение этой проблемы. Но оно не надежное. Т.е. решение надежное, но вот дисциплина у меня хромает. Рассеянный. Хочу что бы проблема не возникала в принципе, либо хоть компилятор бы принуждал к дисциплине. А он молчит
Re[5]: help: практика работы с shared_ptr
От: Mr.Delphist  
Дата: 12.11.10 12:49
Оценка:
Здравствуйте, samius, Вы писали:

S>>>Разве удаление объекта не нарушает его константность?

U>>тут дело в не в смарт-пойнтерах. а в языке
U>>удалять объект разрешено, если есть указатель на конст объект
U>>
U>>const A* const ptr = new A();
U>>delete ptr;
U>>

S>По меньшей мере странно это выглядит. Изменять нельзя, удалять можно.

Что же тут странного? Гарантируется, что в течение свой жизни const-экземпляр будет неизменным (ну, не считая хаков со всякими кастами-мутастами). Но также верно, что каждый порожденный экземпляр имеет конституционное право на смерть. Тут и сказке конец.
Re[6]: help: практика работы с shared_ptr
От: samius Япония http://sams-tricks.blogspot.com
Дата: 12.11.10 12:56
Оценка:
Здравствуйте, Mr.Delphist, Вы писали:

MD>Здравствуйте, samius, Вы писали:


U>>>удалять объект разрешено, если есть указатель на конст объект


S>>По меньшей мере странно это выглядит. Изменять нельзя, удалять можно.


MD>Что же тут странного? Гарантируется, что в течение свой жизни const-экземпляр будет неизменным (ну, не считая хаков со всякими кастами-мутастами).

Если только считать что он умирает мгновенно не изменившись. Но из деструктора можно вызвать любой неконстантный метод, что делает смерть объекта не атомарной.

MD>Но также верно, что каждый порожденный экземпляр имеет конституционное право на смерть. Тут и сказке конец.

Странно что в этой сказке вершить приговор может любой.
Re[9]: help: практика работы с shared_ptr
От: remark Россия http://www.1024cores.net/
Дата: 12.11.10 15:23
Оценка:
Здравствуйте, sokel, Вы писали:

S>только вектор тут ни к чему, я бы как нибудь так сделал:


Согласен, так ещё лучше.


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[10]: help: практика работы с shared_ptr
От: samius Япония http://sams-tricks.blogspot.com
Дата: 12.11.10 15:36
Оценка:
Здравствуйте, remark, Вы писали:

R>Здравствуйте, sokel, Вы писали:


S>>только вектор тут ни к чему, я бы как нибудь так сделал:


R>Согласен, так ещё лучше.


А чем лучше?

Как-то странно. Вроде язык высокоуровневый считается, куча умных контейнеров и указателей в библиотеках.. А как доходит до банального дерева — приходится выпиливать его руками с нуля
Re[11]: help: практика работы с shared_ptr
От: samius Япония http://sams-tricks.blogspot.com
Дата: 12.11.10 15:39
Оценка:
Здравствуйте, samius, Вы писали:

R>>Здравствуйте, sokel, Вы писали:


S>>>только вектор тут ни к чему, я бы как нибудь так сделал:



S>Как-то странно. Вроде язык высокоуровневый считается, куча умных контейнеров и указателей в библиотеках.. А как доходит до банального дерева — приходится выпиливать его руками с нуля


Извиняюсь, пропустил intrusive_list<node_t>
Re[7]: help: практика работы с shared_ptr
От: Centaur Россия  
Дата: 13.11.10 07:35
Оценка:
Здравствуйте, samius, Вы писали:

S>Если только считать что он умирает мгновенно не изменившись. Но из деструктора можно вызвать любой неконстантный метод, что делает смерть объекта не атомарной.


Объект считается мёртвым, как только начал выполняться его деструктор.
Re[11]: help: практика работы с shared_ptr
От: Erop Россия  
Дата: 13.11.10 07:50
Оценка:
Здравствуйте, samius, Вы писали:
S>Как-то странно. Вроде язык высокоуровневый считается, куча умных контейнеров и указателей в библиотеках.. А как доходит до банального дерева — приходится выпиливать его руками с нуля

Если тебе не важна супер-эффективность, то просто пишешь дерево на массиве shared_ptr детей и всё.
Если дерево надо прошить, то добавляешь регистрацию/отписывание родителя в детях.

А вот если ты начинаешь считать ресурсы и жёстко экономить, то тут уже надо подробностями заморочиться...

В каком-то смысле это не обязанность, а возможность
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[8]: help: практика работы с shared_ptr
От: samius Япония http://sams-tricks.blogspot.com
Дата: 13.11.10 18:05
Оценка:
Здравствуйте, Centaur, Вы писали:

C>Здравствуйте, samius, Вы писали:


S>>Если только считать что он умирает мгновенно не изменившись. Но из деструктора можно вызвать любой неконстантный метод, что делает смерть объекта не атомарной.


C>Объект считается мёртвым, как только начал выполняться его деструктор.

Т.е. методы, вызванные из деструктора, выполняются уже у мертвого объекта?

Да не, я не против. Просто нашел это странным.
Re[12]: help: практика работы с shared_ptr
От: samius Япония http://sams-tricks.blogspot.com
Дата: 13.11.10 18:21
Оценка:
Здравствуйте, Erop, Вы писали:

E>Здравствуйте, samius, Вы писали:

S>>Как-то странно. Вроде язык высокоуровневый считается, куча умных контейнеров и указателей в библиотеках.. А как доходит до банального дерева — приходится выпиливать его руками с нуля

E>Если тебе не важна супер-эффективность, то просто пишешь дерево на массиве shared_ptr детей и всё.

на данном этапе не нужна, да и вообще вряд ли.

E>Если дерево надо прошить, то добавляешь регистрацию/отписывание родителя в детях.

Что значит прошить?

E>А вот если ты начинаешь считать ресурсы и жёстко экономить, то тут уже надо подробностями заморочиться...


E>В каком-то смысле это не обязанность, а возможность


Ясно. Но тему я завел еще и что бы понять, как борются с рисками использования смартпоинтеров (вроде пересоздания shared_ptr по одному адресу T*).
Т.е. пока мне чихать на эффективность до той степени что я могу себе позволить вектора shared_ptr. Стоит ли (принято ли) в таких случаях заморачиваться упрятыванием указателей T* от греха подальше? Либо все лечится дисциплиной и передачей исключительно shared_ptr<T> по ссылке либо по значению?
Например в гвайде гугла рекомендуют воздерживаться от shared_ptr (но по другим причинам).
Re[12]: help: практика работы с shared_ptr
От: samius Япония http://sams-tricks.blogspot.com
Дата: 13.11.10 18:33
Оценка:
Здравствуйте, remark, Вы писали:

R>Здравствуйте, samius, Вы писали:


S>>Как-то странно. Вроде язык высокоуровневый считается, куча умных контейнеров и указателей в библиотеках.. А как доходит до банального дерева — приходится выпиливать его руками с нуля


R>Основная сила С++ в том, что можно и так и так.

R>Если делаешь прототип или утилиту, то используешь std::vector, выделяешь всё по new и не удаляешь (получается настоящий GC).
R>А если тебе надо будет этим парсить 100-мегабайтный XML в DOM, то тут интрузивные линки, и region аллокатор огого как пригодятся.
Максимум мегабайтный, но на камне в 200MHz. Сейчас основная проблема заключается в скорости создания прототипа. А я итак закис в деталях

R>

:кефир:
Re[13]: help: практика работы с shared_ptr
От: Erop Россия  
Дата: 14.11.10 13:16
Оценка:
Здравствуйте, samius, Вы писали:

E>>Если дерево надо прошить, то добавляешь регистрацию/отписывание родителя в детях.

S>Что значит прошить?

Добавить детям указатель на родителя.
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.