Указатель на вектор vs вектора указателей против указателя на указатель указателей

Просто интересно, что вы считаете лучшей практикой относительно векторов в С++.

Если у меня есть класс, содержащий переменную-член вектора. Когда этот вектор должен быть объявлен:

  • "Всеобъектный" векторный член varayable, содержащий значения, т.е. vector<MyClass> my_vector;
  • Указатель на вектор, т.е. vector<MyClass>* my_vector;
  • Вектор указателей, т.е. vector<MyClass*> my_vector;
  • Указатель на вектор указателей, т.е. vector<MyClass*>* my_vector;

У меня есть конкретный пример в одном из моих классов, где я в настоящее время объявлял вектор как случай 4, т.е. vector<AnotherClass*>* my_vector; где AnotherClass - это еще один из классов, которые я создал.

Затем в списке инициализации моего конструктора я создаю вектор, используя new:

MyClass::MyClass()
: my_vector(new vector<AnotherClass*>())
{}

В моем деструкторе я делаю следующее:

MyClass::~MyClass()
{
  for (int i=my_vector->size(); i>0; i--)
  {
    delete my_vector->at(i-1);
  }
  delete my_vector;
}

Элементы векторов добавляются в один из методов моего класса. Я не знаю, сколько объектов будет добавлено в мой вектор заранее. Это определяется, когда код выполняется, на основе анализа XML файла.

Это хорошая практика? Или должен ли вектор быть объявлен как один из других случаев 1, 2 или 3?

Когда использовать какой случай?

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

Большое спасибо!

Ответ 1

Обычно решение 1 - это то, что вы хотите, так как оно является самым простым в С++: вам не нужно заботиться об управлении памятью, С++ делает все это для вас (например, вам тогда не понадобилось бы предоставить какой-либо деструктор).

Существуют конкретные случаи, когда это не работает (особенно в случае работы с полиморфными объектами), но в целом это единственный хороший способ.

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

Ответ 2

Определенно первый!

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

Что касается типа значения: все контейнеры в основном принимают значение-семантику. Опять же, вам нужно будет управлять памятью при использовании указателей, и это векторная цель сделать это для вас. Это также описано в п. 79 из книги Стандарты кодирования С++. Если вам нужно использовать совместное владение или "слабые" ссылки, используйте вместо этого соответствующий интеллектуальный указатель.

Ответ 3

Удаление всех элементов в векторе вручную является анти-шаблоном и нарушает идиом RAII в С++. Поэтому, если вам нужно хранить указатели на объекты в vector, лучше использовать "умный указатель" (например, boost::shared_ptr), чтобы облегчить разрушение ресурсов. boost::shared_ptr, например, вызовы delete автоматически при уничтожении последней ссылки на объект.

Также нет необходимости выделять MyClass::my_vector с помощью new. Простое решение:

class MyClass {

   std::vector<whatever> m_vector;
};

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

Во многих случаях вы даже можете использовать простой std::vector<MyClass> - тот, когда объекты в векторе безопасны для копирования.

Ответ 4

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

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

Вектор указателей полезен в случае полиморфных объектов, но есть альтернативы, которые вы должны рассмотреть:

  • Если вектор принадлежит объектам (это означает, что их время жизни ограничено вектором), вы можете использовать boost::ptr_vector.
  • Если объекты не принадлежат вектору, вы можете либо использовать вектор boost::shared_ptr, либо вектор boost::ref.

Ответ 5

Указатель на vector очень редко используется - a vector дешево построить и уничтожить.

Для элементов в vector нет правильного ответа. Как часто изменяется vector? Сколько стоит копировать-конструировать элементы в vector? У других контейнеров есть ссылки или указатели на элементы vector?

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

Считающий смарт-указатель, например boost::shared_ptr, скорее всего, будет лучшим выбором, если в противном случае вам понадобится использовать указатели в качестве элементов vector.

Ответ 6

Сложный ответ: это зависит.

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