Std::vector:: clear() в конструкторе и деструкторе
Я встречаюсь много раз с кодом, где std::vector:: clear() члена класса типа std::vector вызывается в конструкторе и деструкторе.
Я не понимаю, зачем это нужно:
- конструктор - член класса типа std::vector по умолчанию пуст, поэтому нет необходимости вызывать clear().
- деструктор - член класса типа std::vector будет уничтожен как часть стандартного уничтожения объекта, сопряженного с ним. В рамках уничтожения вектора все объекты значения, содержащиеся в нем, будут уничтожены (если он кучи выделенных указателей на память, их следует удалить "вручную" ), поэтому снова не нужно вызывать clear().
Я что-то пропустил?
Ответы
Ответ 1
Из звука вещей люди, которые писали этот код, были теми, кто что-то пропустил. Единственный раз, когда было бы разумно называть clear() в ctor или dtor, было бы посредине другого кода. Например, ctor может читать некоторые данные, обрабатывать их, а затем читать больше данных. В таком случае, вероятно, быстрее использовать один контейнер для данных во время чтения и каждый раз очищать его, чем создавать каждый контейнер на каждой итерации.
Ответ 2
Нет, вы ничего не пропустили. Я подозреваю, что это (безобидное) программирование вуду, вроде как установка указателя на нуль после его освобождения или случайного вызова repaint/revalidate в графическом интерфейсе. Программист помнит, что в прошлом он помогал с какой-то ошибкой и теперь добавляет его излишне "на всякий случай". Кто знает, может, это поможет. Voodoo.
Ответ 3
- Он ПОЛНОСТЬЮ нечетко очищает содержимое контейнера stl в конструкторе
- Неприкосновенно очищать содержимое контейнера stl в деструкторе БЕЗ ОШИБКИ. Контейнер содержит указатель. Если указатель был создан с использованием new, он все равно должен быть удален. После этого он все равно не будет незаметным для .clear контейнера.
Рассмотрим это:
#define BOOST_TEST_MODULE StlContainers
#define BOOST_LIB_DIAGNOSTIC
#include <boost/test/unit_test.hpp>
#include <boost/assign.hpp>
#include <boost/assign/list_of.hpp>
#include <boost/assign/std/vector.hpp>
#include <vector>
using namespace boost::assign;
using namespace std;
const vector<int> my_ints_vector = list_of(0)(1)(1)(2)(3)(5)(8)(13)(21)(34);
struct ScopedStruct1
{
ScopedStruct1(const vector<int> & v) : m_v(v) {}
~ScopedStruct1() {}
private :
vector<int> m_v;
};
class A
{
public :
A(int i) : m_i(i) {}
~A() {}
private :
int m_i;
};
struct ScopedStruct2
{
ScopedStruct2() {}
~ScopedStruct2() { for(vector<A*>::iterator it = m_v.begin(); it != m_v.end(); ++it) delete *it; }
vector<A*> m_v;
};
struct ScopedStruct3
{
ScopedStruct3() {}
~ScopedStruct3() { /* no deletion */ }
vector<A*> m_v;
};
BOOST_AUTO_TEST_CASE(StlContainer_storing_something_simple)
{
ScopedStruct1 str(my_ints_vector);
}
BOOST_AUTO_TEST_CASE(StlContainer_storing_pointers_with_delete)
{
ScopedStruct2 str;
for(int i = 0; i < 10; i++)
str.m_v.push_back(new A(i));
}
BOOST_AUTO_TEST_CASE(StlContainer_storing_pointers_without_delete)
{
ScopedStruct3 str;
for(int i = 0; i < 10; i++)
str.m_v.push_back(new A(i));
}
Используя boost unit_test framework, я создал 3 тестовых примера. Рамка unit_test является greate, потому что она отслеживает утечки памяти.
Вы заметите, что 1-й и 2-й тестовые примеры не генерируют утечки памяти, но третий случай делает это, потому что содержимое вектора не удаляется.
Ответ 4
Нет, ты прав. Если в конструкторе (или конструкторе базовых классов) нет необходимости в дополнительном бизнесе, но это очень мало...
Позже отредактируйте
В случае деструктора одна из наиболее распространенных ошибок, которые я видел, заключается в том, что некоторые люди предполагают, что ясный метод также вызовет delete для векторов указателей (векторов), что, конечно же, не так.
Ответ 5
Единственный случай, когда я могу думать о том, где это было бы полезно, - это когда порядок разрушения имеет значение, а деструктор хочет, чтобы объекты в векторе были уничтожены до чего-то другого.
Конечно, лучше структурировать код, чтобы не требовать этого; однако это разумная причина.
Ответ 6
Несмотря на то, что было сказано до сих пор, существует хотя бы один сценарий, когда может потребоваться явный вызов clear
в деструкторе.
Представьте себе ситуацию, когда разрушаемый объект имеет несколько субобъектов-членов, которые требуют определенного порядка уничтожения, т.е. подобъекты каким-то образом зависят друг от друга, а неправильный порядок их уничтожения приведет к нежелательным результатам. Как вы, вероятно, знаете, порядок уничтожения субобъектов членов (а также инициализация членов) определяется порядком декларации членов в определении класса. Таким образом, одним из способов достижения надлежащего порядка уничтожения является организация деклараций участников соответственно. Однако, во-первых, это не очень хорошее решение для обслуживания. Во-вторых, желаемый порядок разрушения может зависеть от некоторых условий выполнения. В-третьих, желаемый порядок уничтожения может противоречить желаемому начальнику инициализации. Все это означает, что было бы невозможно (или мудрый) командовать надлежащим порядком уничтожения путем повторной организации деклараций.
Разумным подходом в этом случае может быть очистка некоторых субобъектов критических элементов вручную, вызывая их методы clean
или такие, до тех пор, пока зависимость порядка уничтожения не исчезнет. Я бы предположил, что, возможно, код, который вы видели, пытается решить проблему заказа, вызывая clean
в стратегически подобранном подобъекте vector
.
Как для вызова clean
в конструкторе... Я понятия не имею, почему кто-то будет делать что-то подобное.
Ответ 7
Конечно, нужно вызывать clear() или изменить размер (0) или эквивалент say (std:: _ Destroy_range (...) в деструкторе перед освобождением.
Отмена выполняется через allocator:: deallocate , который не запускает никаких деструкторов. Он просто освобождает память.
clear() эквивалентно изменению размера (0), который запускает деструкторы для объектов первого размера() в выделенном буфере
Не только выделенные указатели, дескрипторы файлов, удерживаемые мьютексы, все остальные восстанавливаемые ресурсы, удерживаемые объектом. Деструкторы ДОЛЖНЫ запускаться. До создания экземпляра шаблон не знает, что деструктор тривиален. Если деструктор тривиален, THEN его оптимизируется ПОСЛЕ инстанцирования