C ++: создание общего объекта, а не общего указателя на объект - PullRequest
4 голосов
/ 10 декабря 2010

boost :: shared_ptr действительно беспокоит меня.Конечно, я понимаю полезность такой вещи, но я хотел бы, чтобы я мог использовать shared_ptr<A> как и A*.Рассмотрим следующий код

class A
{
public:
    A() {}
    A(int x) {mX = x;}
    virtual void setX(int x) {mX = x;}
    virtual int getX() const {return mX;}
private:
    int mX;
};


class HelpfulContainer
{
public:
    //Don't worry, I'll manager the memory from here.
    void eventHorizon(A*& a)
    {
        cout << "It's too late to save it now!" << endl;
        delete a;
        a = NULL;
    }
};


int main()
{
    HelpfulContainer helpfulContainer;

    A* a1 = new A(1);
    A* a2 = new A(*a1);
    cout << "*a1 = " << *a1 << endl;
    cout << "*a2 = " << *a2 << endl;
    a2->setX(2);
    cout << "*a1 = " << *a1 << endl;
    cout << "*a2 = " << *a2 << endl;
    cout << "Demonstrated here a2 is not connected to a1." << endl;

    //hey, I wonder what this event horizon function is.
    helpfulContainer.eventHorizon(a1);

    cout << "*a1 = " << *a1 << endl;//Bad things happen when running this line.
}

Кто бы ни создал HelpfulContainer, он не думал о других, желающих сохранить указатели на объекты A.Мы не можем предоставить объекты HelpfulClass boost :: shared_ptr.Но одну вещь, которую мы могли бы сделать, это использовать идиому pimlp для создания SharedA, который сам по себе является A:

class SharedA : public A
{
public:
    SharedA(A* a) : mImpl(a){}
    virtual void setX(int x) {mImpl->setX(x);}
    virtual int getX() const {return mImpl->getX();}
private:
    boost::shared_ptr<A> mImpl;
};

И тогда основная функция может выглядеть примерно так:

int main()
{
    HelpfulContainer helpfulContainer;

    A* sa1 = new SharedA(new A(1));
    A* sa2 = new SharedA(sa1);
    cout << "*sa1 = " << *sa1 << endl;
    cout << "*sa2 = " << *sa2 << endl;
    sa2->setX(2);
    cout << "*sa1 = " << *sa1 << endl;
    cout << "*sa2 = " << *sa2 << endl;
    cout << "this demonstrates that sa2 is a shared version of sa1" << endl;

    helpfulContainer.eventHorizon(sa1);
    sa2->setX(3);
    //cout << "*sa1 = " << *sa1 << endl;//Bad things would happen here
    cout << "*sa2 = " << *sa2 << endl; 
    //but this line indicates that the originally created A is still safe and intact.
    //only when we call sa2 goes out of scope will the A be deleted.
}

Итак, мой вопрос таков: Является ли приведенный выше шаблон хорошим шаблоном, или я что-то еще не рассматривал.Мой текущий проект унаследовал класс HelpfulContainer, как описано выше, который удаляет нужные мне указатели, но мне все еще нужна структура данных, присутствующая в HelpfulContainer.


Обновление: этот вопрос это дополнительный вопрос.

Ответы [ 4 ]

6 голосов
/ 10 декабря 2010

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

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

Если вам абсолютно необходимо разделить права собственности между двумя вещами, которые не взаимодействуют (например, shared_ptr и HelpfulContainer), то вам придется использовать какую-то технику прокси.

В этом случае, однако, это выглядит просто как HelpfulContainer, но это не очень полезно для вашей ситуации.

1 голос
/ 10 декабря 2010

Я не уверен, что это для вас.

Если helpfulContainer.eventHorizon() всегда удаляет свой параметр, то почему бы просто не передать новую копию (оригинал) класса A:

  helpfulContainer.eventHorizon(new A(sa1));

Или, если helpfulContainer.eventHorizon() только иногда удаляет свой параметр, то при вызове

  helpfulContainer.eventHorizon(new SharedA(sa1)); 

произойдет утечка обоих SharedA и оригинал A (sa1) в тех случаях, когда он решает не удалять.

0 голосов
/ 10 декабря 2010

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

Мне кажется, что HelpfulContainer - это что-то НО полезное и должно быть исправлено или исключено.

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

0 голосов
/ 10 декабря 2010

Итак, вы создаете Stand-In (SharedA), для которого все в порядке. Несмотря на то, что это немного неловко, я думаю, что необходимо работать с устаревшим API. Чтобы немного улучшить это: разрешите создание SharedA из shared_ptr, но не наоборот - и затем используйте SharedP только тогда, когда вам абсолютно необходимо:

int main()
{
  HelpfulContainer helpfulContainer;

  boost::shared_ptr<A> sa1(new A(1));

  // deletes its parameter, but that's okay
  helpfulContainer.eventHorizon(new SharedA(sa1)); 
}
...