Общие указатели, хранящиеся в векторе интеллектуальных указателей внутри другого объекта, хранящегося в общем указателе (Shareption)

В настоящее время я работаю над новым проектом со своей командой в Cpp, и мы решили попрощаться со старомодными необработанными указателями и попробовать умные указатели.

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

Дело в том, что я начал с основ, а потом застрял на этом.

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

shared_ptr.cpp:

#include <iostream>
#include <memory>
#include <vector>
#include <string>

using namespace std;

class Object{

public:

    vector<shared_ptr<Object>> list;
    string name;
    Object(string name){
        this->name=name;
    }
    ~Object(){
        cout<<"...Deleting "<<name<<endl;
    }
};

int main(){

    shared_ptr<Object> o1 = make_shared<Object>("o1");
    shared_ptr<Object> o2 = make_shared<Object>("o2");

    o1->list.push_back(shared_ptr<Object>(o2));
    o2->list.push_back(shared_ptr<Object>(o1));

}

инструкции для компилятора

g++ shared_ptr.cpp -o shared.o && ./shared.o

Мой вопрос здесь: не должен ли общий указатель обрабатывать саму ссылку? Или я должен очистить внутренний вектор до завершения программы?

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

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

Спасибо!


person M. Martini    schedule 15.08.2019    source источник
comment
За исключением очень узких обстоятельств, свободное использование общих указателей в коде C++ указывает либо на плохой дизайн, либо на тот факт, что люди, разрабатывающие систему, не хотели тратить время и думать о владении объектами и времени жизни. Даже с общими указателями C++ — это не Java, так что об этом нужно помнить. Кроме того, совершенно нормально использовать необработанные указатели, не являющиеся владельцами.   -  person SergeyA    schedule 15.08.2019
comment
Не по теме, но ненужно делать явные копии при пуше в векторы. Все, что он делает, это тратит больше времени на копирование.   -  person molbdnilo    schedule 15.08.2019
comment
Что бы вы сделали, если бы это были необработанные указатели? Нетрудно преобразовать все, что вы делаете с необработанными указателями, в эквивалентный код, обрабатывающий интеллектуальные указатели. Например, вы бы собрали все экземпляры Object и отключили бы их?   -  person David Schwartz    schedule 15.08.2019


Ответы (2)


Общие указатели — это указатели с подсчетом ссылок.

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

Общие указатели моделируют общепринятое представление об общем «владении», если A однозначно ссылается на B с использованием общего указателя, можно думать об A как о владении B. Если A и C оба ссылаются на B с помощью общих указателей, тогда мы можем думать об A и C как совместное владение B. Если ваши данные не соответствуют такому понятию владения, вам не следует использовать общие указатели. В частности, в общих ориентированных графах нельзя сказать, что отдельные вершины «владеют» своими соседями, поскольку общие графы могут иметь произвольную топологию; у них нет естественной иерархии; в произвольных графах нет «родителей» и «детей». То есть такие графы могут иметь циклы.

С ориентированными ациклическими графами дело обстоит иначе: их вершины могут быть упорядочены в так называемом «топологическом порядке», в котором родители предшествуют детям. В определенном смысле группы вершин владеют другими вершинами.

Это не означает, что нет способа управлять памятью, выделенной вершинами общего графа, с помощью интеллектуальных указателей, просто вы не можете сделать это, рассматривая его вершины как общих владельцев дочерних вершин. Например, вы можете создавать вершины с фабрикой, которая поддерживает ссылки на то, что она создает, как набор std::unique_ptrs, и использовать необработанные указатели для ссылок от вершины к вершине. Или вы можете сохранить свой граф как группу DAG из shared_ptrs, но использовать weak_ptrs для «обратных указателей» от дочерних элементов к родительским.

Какой дизайн лучше, зависит от специфики того, что вы делаете.

person jwezorek    schedule 15.08.2019

Мой вопрос здесь: не должен ли общий указатель обрабатывать саму ссылку? Или я должен очистить внутренний вектор до завершения программы?

Есть много решений для этого. Конечно, std::weak_ptr может разорвать цикл, но иногда это другой вопрос.

Возьмите это, например:

struct Graph {
    struct Node {
        std::array<std::shared_ptr<Node>, 4> adjacent;
    };

    std::vector<std::shared_ptr<Node>> nodes;
};

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

Здесь ясно, что у нас есть владелец всех узлов: класс Graph.

Если мы напишем тот же код, где Graph будет уникальным владельцем узлов, мы получим следующее:

struct Graph {
    struct Node {
        std::array<Node*, 4> adjacent;
    };

    std::vector<std::unique_ptr<Node>> nodes;
};

Когда граф умирает, он берет с собой узлы.

Буууууу! Что происходит, когда я делаю это:

Node n = *get_graph_copy().nodes[1];
n.adjacent[0]; // boom!

Ну, ты нарушил контракт. Есть ли смысл иметь узел без графа?

person Guillaume Racicot    schedule 15.08.2019