Вопрос о дизайне класса C ++ - PullRequest
       1

Вопрос о дизайне класса C ++

1 голос
/ 14 сентября 2011

это вопрос о том, какой класс освобождает общие указатели.

Итак, у меня есть иерархия классов, base и derivedA, derivedB и derivedC, все из base. База имеет несколько виртуальных функций.

У меня есть класс Holder, у него есть коллекция, в которой хранятся экземпляры derivedA, derivedB и derivedC. Таким образом, создается экземпляр Holder, экземпляры производных классов создаются динамически и добавляют свои указатели в контейнеры. Я добавляю указатели, чтобы перебирать контейнер и вызывать base->virtualFunction. Я new объекты, потому что в противном случае экземпляр, созданный в стеке, уничтожается, когда выходит из области видимости.

class Base;
class DerivedA : public base;
class DerivedB : public base;
class DerivedC : public base;


class Holder {
    std::vector<Base*> collection;
    void add(Base* base);
}

Holder holder;
DerivedA* da = new DerivedA;
DerivedB* db = new DerivedB;

holder.add(da);
holder.add(db);

кому звонить delete на da и db?

Любой другой способ создать это для проблемы удаления исчезает?

Спасибо Реза

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

Ответы [ 6 ]

3 голосов
/ 14 сентября 2011

Более простой способ? Измените свой вектор, чтобы стать осведомленным владельцем.

boost::ptr_vector<Base>

- это такой вектор, особенно подходящий для полиморфных данных:

  • глубокое копирование с помощью методов клонирования (просто реализуйте virtual Base* clone() const;)
  • автоматическое управление памятью
  • сахарное покрытие на итераторе разыменования (дает Base& вместо Base*)

В C ++ 11 другой альтернативой может быть std::vector< std::unique_ptr<Base> >.

3 голосов
/ 14 сентября 2011

Если вы используете new, тогда вы должны сказать delete, чтобы убрать.

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

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

Лучшим подходом к проектированию было бы заключить результат new в менеджер с единственной ответственностью, такой как shared_ptr или unique_ptr, и сохранить те в контейнере. Таким образом, всегда совершенно ясно, кому принадлежат объекты, выделенные вручную, и кто распоряжается ими, если ссылка станет недоступной или потерянной.


[ Оригинальное обновление: ] В качестве предложения для вашего оригинального дизайна, который может или не может быть уместным (в зависимости от многих других факторов, о которых вы не говорите нам), и несмотря на все его недостатки, вы можете заставить Holder выполнить очистку и указать в договоре, что вы обещаете add только указатели, которые вы создали с new:

Holder::~Holder() {
  for (auto it = collection.cbegin(); it != collection.cend(); ++it)
    delete *it;
}

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

2 голосов
/ 14 сентября 2011

Ответственность за освобождение памяти несет класс / метод, который знает, используются ли эти объекты по-прежнему / полезны.

В вашем простом примере кода Holder содержит только указатели на объекты, поэтому он можетвозможно не знает, когда освободить память: заостренные объекты могут быть полезны даже после удаления Holder holder.

2 голосов
/ 14 сентября 2011

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

Одна возможность для Holder - принять владение классами и затем выполнить очистку в ~Holder (вызывая delete для каждого элемента в векторе). Но тогда вам также нужно будет решить, как копировать Holder (глубокая или обычная копия). Второй вариант потребует от вас отслеживать все HolderS, которые имеют ссылки на одну и ту же память и т. Д.

Коротко и просто: используйте shared_ptr или избегайте этой ситуации все вместе. Они доступны в компиляторах с поддержкой C ++ 11 или в библиотеках Boost.

1 голос
/ 14 сентября 2011

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

1 голос
/ 14 сентября 2011

То, что вы пытаетесь сделать, было реализовано разумным и проверенным способом Boost Smart Pointers , я настоятельно рекомендую всегда сначала смотреть на boost.org, если это еще не было сделано, уже.

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