Срок службы элементов контейнеров STL - PullRequest
4 голосов
/ 14 марта 2012

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

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

vector<MyClass> myVec;
myVec.push_back(MyClass(...));

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

Наиболее прямой альтернативой является использование указателей для хранения динамически размещаемых объектов, но тогда деструктор MyClass не будет вызываться при уничтожении вектора.Хранение auto_ptr вместо обычных указателей дает ошибку в myVec.push_back (...) .

Есть ли способ избежать первого выбора при наличии деструктора контейнераназывать элементы деструктором?

Спасибо за ваши ответы!

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

Рассмотреть аналогичную проблему;как реализовать контейнер, владеющий объектами, используя абстрактный базовый класс.Уникальный указатель (Boost's unique_ptr) не имеет конструкторов копирования, поэтому его нельзя использовать напрямую.

class A {};             // Abstract base class.
class B : public A {};  // Sub class.

...


vector<A *> vec;
vec.push_back(new B());

// At destruction of vec, destroy elements left in container.

Ответы [ 5 ]

4 голосов
/ 14 марта 2012

Альтернативой, еще не упомянутой, является библиотека контейнеров с указателями ускорения .

Boost.Pointer Container предоставляет контейнеры для хранения выделенной кучи объекты в исключительной безопасности и с минимальными издержками. Цель библиотеки, в частности, для облегчения ОО-программирования на C ++ путем установления стандартного набора классов, методов и конструкций для решение особых проблем ОО

С помощью boost::ptr_vector вместо отсылки копий назад вы нажимаете указатели на динамически размещаемые объекты. ptr_vector становится владельцем этих объектов и гарантирует их удаление при удалении самого ptr_vector. Клиенты, которые читают из ptr_vector, используют тот же интерфейс, что и обычный std::vector, поэтому им не приходится иметь дело с указателями. Например, boost::ptr_vector<T>::front() возвращает ссылку.

Раздел «Мотивация» документации поможет вам решить, подходит ли вам это решение.

3 голосов
/ 14 марта 2012

C ++ 11 имеет emplace_back, который отлично перенаправит все, что вы дадите, конструктору элементов и создаст его непосредственно на месте :

#include <vector>
#include <iostream>

struct X{
  X(int i, float f, bool b){
    std::cout << "X(" << i << ", " << f << ", " << b << ")\n";
  }
};

int main(){
  std::vector<X> vx;
  vx.emplace_back(42, 3.14f, true);
}

Живой пример на Ideone.

В C ++ 03 вам не повезло, и вам приходится жить с вызовом copy ctor (точнее, два - один в параметре, один во внутренний массив вектора). Если ваш класс разработан правильно, это должно быть лишь незначительным неудобством эффективности.

2 голосов
/ 14 марта 2012

Наилучший способ - использовать элементы по значению в контейнерах стандартной библиотеки.
Контейнеры стандартной библиотеки работают на семантике значения . то есть: они хранят элементы по значению, и вы точно знаете, что контейнер становится владельцем элементов. Так что вам не нужно явное ручное управление памятью.

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

В случае указателя в качестве элементов контейнера необходимо вручную управлять памятью и явно отменить выделение динамического выделения.
В этом случае, когда Вам необходимо, чтобы объект находился в динамической памяти, Вам следует использовать Умные указатели .
auto_ptr устарело и не может использоваться в контейнерах стандартной библиотеки, так как имеет неинтуитивное поведение при назначении. unique_ptr - лучший выбор для auto_ptr, предложенный новым стандартом c ++ 11.

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

1 голос
/ 14 марта 2012

Если вы хотите хранить указатели, которые мне нравятся boost::ptr_vector

Это действует как вектор, но сохраняет и получает владение указателями.

Преимущество boost::ptr_vector<X> над std::vector<some_smart_ptr<X>> заключается в том, чтоэлемент доступа к ptr_vector возвращает ссылку на объект, а не ссылку на (умный) указатель.Это упрощает использование контейнера со стандартными алгоритмами (поскольку вам не нужно связывать функтор для разыменования элемента).

Но если нет очень веской причины, все же лучше хранить объект по значению вобычный std::vector.

Уважительные причины могут включать:

  • Очень дорого копировать.
  • Невозможно использовать идеальную пересылку для создания на месте.
  • В контейнере должны храниться полиморфные объекты.
0 голосов
/ 14 марта 2012

Звучит так, как будто вы хотите иметь одно значение, которое будет иметь срок службы, эквивалентный содержанию vector<T>. Если это так, то это хорошее место, чтобы рассмотреть возможность использования std::shared_ptr<T>, который является типом указателя с подсчетом ссылок

typedef std::shared_ptr<MyClass> MyClassPtr;
...
vector<MyClassPtr> myVec;
myVec.push_back(new MyClass(...));
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...