Безопасно / не рекомендуется создавать общие указатели из слабого указателя

Вступление

У меня есть два класса, которые я называю Node а также Point Вот. Точки - это объекты, которые содержат конкретную информацию (какая именно информация не имеет значения, я использую в примере простое целое число). Узлы - это объекты, которые управляют несколькими точками, если они должны совместно использовать одну и ту же информацию. Так Node имеет список указателей на все точки, которыми он управляет (что хорошо, потому что Pointудаляет свою ссылку из узла, если его время жизни заканчивается), и каждая точка имеет обратную ссылку на сам узел. Это создает круговую ссылку, которая делает вещи немного сложнее. Я не использую многопоточность, поэтому гонки любого рода не являются проблемой. Кроме того, я обязан использовать C++03 (то есть C++98, в основном) и повысить 1,53, поэтому есть ограничения по возможностям, которые я могу использовать.

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

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

Эта проблема

Я еще не описал "обратную ссылку на узел", потому что именно здесь начинается проблема. Итак, на данный момент я согласился с этим решением:

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

p->node = this->weakThis.lock();

но я не знаю, является ли это просто плохой практикой или содержит серьезные проблемы, которых я не вижу, поскольку ссылка на weak_ptr::lock() только говорит о том, что он создает новый общий объект, несмотря на его запутанное имя, или если это приемлемый код,

Другие решения, о которых я думал

  • Все классы получают общие указатели

    • хорошо: нет необходимости создавать общие объекты из слабых
    • плохой: общий объект внутри класса должен быть явно удален (как, например, при вызове метода Node::destroySelfReference() о котором можно легко забыть. В противном случае объект мог бы жить вечно со ссылкой на себя.
  • Все классы получают слабые указатели

    • хорошо: нам просто нужно скопировать слабый указатель, а не делиться собственностью
    • плохо: каждый раз, когда мы хотели получить доступ к узлу из точки, которую нам нужно было вызвать lock(), И так как ссылка в Point на самом деле не используется в качестве наблюдателя, но используется как общая ссылка, это устарело.

Вопрос

Каково было бы лучшее решение здесь? И каковы их реальные опасности?

Код

/* Using boost 1.53 */
#include <vector>
#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>
#include <boost/weak_ptr.hpp>

using boost::shared_ptr;
using boost::make_shared;
using boost::weak_ptr;
using std::cout;
using std::endl;

/*-------------Classes-----------*/

class Point;

class Node {
private:
    std::vector<Point *> points;
    weak_ptr<Node> weakThis;
public:
    int value;
    Node(Point * p, shared_ptr<Node> & sp);
    void add(Point * p);
    void remove(Point * p);
    int getValue() {
        return this->value;
    }
};

class Point {
private:
    int value;
public:
    shared_ptr<Node> node;
    Point() : value(2) {}
    ~Point() {
        if(this->node != NULL) {
            this->node->remove(this);
        }
    }
    int getValue() {
        if (this->node == NULL) return this->value;
        else {
            return this->node->getValue();
        }
    }
};

/*----------Node definition------------*/

Node::Node(Point * p, shared_ptr<Node> & sp) : value(1) {
    sp.reset(this);
    this->weakThis = sp;
    this->add(p);
}

void Node::add(Point * p) {
    this->points.push_back(p); /* simplified without checking if element exists since it is unnecessary for the problem */
    p->node = this->weakThis.lock(); /* <- critical part */
}

void Node::remove(Point * p) {
    std::vector<Point *>::iterator it = std::find(this->points.begin(), this->points.end(), p);
    if (it != this->points.end()) {
        Point * pr = *it;
        this->points.erase(it);
        pr->node.reset();
    }
}

/*------------main-----------*/

int main() {
    Point p;
    cout << "Value of unmanaged p is " << p.getValue() << endl;
    shared_ptr<Node> n;
    new Node(&p, n); /* This is a bit strange too but other creations will not work */
    cout << "Added p to n" << endl;
    cout << "Value of managed p is " << p.getValue() << endl;

    n->remove(&p);
    n.reset();
    return 0;
}

0 ответов

Другие вопросы по тегам