2014-01-23 3 views
0

Предположим, что T содержит массив, размер которого может меняться в зависимости от инициализации. Я передаю указатель на вектор, чтобы избежать копирования всех данных и инициализировать следующим образом:Управление памятью с помощью std :: vector <T*>

for(int i=10; i < 100; i++) 
    std::vector.push_back(new T(i)); 

На выходе, один delete s элемента вектора. Есть ли риск потери памяти, если данные, содержащиеся в T, также являются указателями, даже если есть хорошие деструкторы? Например:

+0

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

+1

Почему бы вам не использовать вектор внутри 'T', чтобы избавиться от трудностей с [Правило трех] (http://stackoverflow.com/questions/4172722) ? И почему бы не использовать 'vector ', чтобы избежать проблем с удалением элементов при их удалении? –

+1

Итак, 'T' имеет базовый массив типа' M', и вы сохраняете указатели на 'T' в' std :: vector'? Почему бы просто не использовать 'std :: vector >' и избежать всего лишнего ручного управления памятью? – Chad

ответ

3

Есть две основные проблемы, связанные с вашим классом T:

  • используется delete вместо delete [], чтобы удалить массив, что дает неопределенное поведение
  • Вы не реализуют (или удалить) копию конструктора и оператор копирования-присваивания (по Rule of Three), поэтому существует опасность того, что два объекта попытаются удалить один и тот же массив.

. Оба эти решения можно легко решить, используя std::vector, вместо того, чтобы писать свою собственную версию.

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

2

Ответ: не надо.

Либо использовать

std::vector<std::vector<M>> v; 
v.emplace_back(std::vector<M>(42)); // vector of 42 elements 

или (гадость)

std::vector<std::unique_ptr<M[]>> v; 
// C++11 
std::unique_ptr<M[]> temp = new M[42]; // array of 42 elements 
v.emplace_back(temp); 
// C++14 or with handrolled make_unique 
v.emplace_back(std::make_unique<M[]>(42); 

которые оба делают все для вас с минимальными затратами (особенно последний).

Обратите внимание, что вызов emplace_back с аргументом new не совсем безопасен для вас, как вы хотели бы, даже если результирующий элемент будет умным указателем. Чтобы это сделать, вам необходимо использовать std::make_unique, который находится на C++ 14. Существуют различные реализации, и для этого нет ничего особенного. Он просто был исключен из C++ 11 и будет добавлен в C++ 14.

+0

Является ли проблема не совсем безопасной, поскольку существует вероятность исключения из построения массива M и построения его оболочки unique_ptr? – JAB

+1

@JAB Да, это происходит здесь, когда вектор бросает, когда он изменяет размер, чтобы разместить новый элемент, и оставляет вашу выделенную память страдать и умирать одинокой смертью. См. Комментарии [здесь] (http://stackoverflow.com/a/3283795/256138) для обсуждения и [здесь] (http://stackoverflow.com/q/19472550/256138) для получения дополнительных примеров того, когда он может укусить вы. – rubenvb

+0

@rubenvb Итак, в чем разница между 'std :: make_unique (new M [42])' и 'std :: unique_ptr (новый M [42])'? 'make_unique' будет более безопасным только тогда, когда он не получит выделенный указатель, но называет' new' сам. Кроме того, когда это всего лишь один параметр, нет никакой разницы между созданием временного 'unique_ptr' и использованием' make_unique', использование последнего только сделает код более последовательным. –

Смежные вопросы