Должен ли я всегда вызывать vector clear() в конце функции?

У меня есть простая функция, которая использует такой вектор (псевдокод):

void someFunc(void) {

    std::vector<std::string> contentVector;

    // here are some operations on the vector

    // should I call the clear() here or this could be ommited ?
    contentVector.clear();

}

Должен ли я вызывать clear() или это можно опустить?


person Michal Przybylowicz    schedule 05.02.2014    source источник
comment
Деструктор выполнит очистку. :)   -  person Borgleader    schedule 05.02.2014
comment
Это лучше, чем могло бы, это должно быть опущено !! В этом весь смысл деструктора и RAII. Пусть очисткой занимается деструктор, вам тут делать нечего. Это здорово, не так ли?   -  person Davidbrcz    schedule 05.02.2014
comment
Этот вопрос демонстрирует убийственную функцию C++: RAII. В хорошо спроектированном объекте ресурсы объекта привязаны к времени жизни объекта, и это явно поддерживается в языке.   -  person Kaz Dragon    schedule 05.02.2014


Ответы (5)


Если мы посмотрим на запись cppreference.com для std::vector::~vector говорит:

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

так что нет, вам не нужно вызывать clear.

Если мы хотим перейти к проекту стандарта, мы должны просмотреть раздел 23.2.1 Общие требования к контейнерам параграф 4, в котором говорится:

В таблицах 96 и 97 X обозначает класс-контейнер, содержащий объекты типа T, a и b обозначают значения типа X, [...]

а затем посмотрите на Table 96 — Container requirements, который имеет следующую запись выражения:

(&a)->~X()  

и следующее примечание:

примечание: деструктор применяется к каждому элементу a; вся память освобождается.

Обновить

Это RAII в действии, как говорит Бьерн Страуструп в Почему в C++ нет конструкции "finally"?:

Потому что C++ поддерживает альтернативу, которая почти всегда лучше: метод «получение ресурсов — это инициализация» (TC++PL3, раздел 14.4). Основная идея состоит в том, чтобы представить ресурс локальным объектом, чтобы деструктор локального объекта освободил ресурс. Таким образом, программист не может забыть освободить ресурс.

person Shafik Yaghmour    schedule 05.02.2014

В этом нет абсолютно никакой необходимости. std::vector и все остальные контейнеры автоматически уничтожают свои элементы, когда они сами должны были быть уничтожены. Это означает, что их деструкторы несут ответственность за это действие. Так что не надо.

Прелесть этого в том, что контейнеры естественным образом защищены от исключений[1]:

void someFunc(void) {

    std::vector<std::string> contentVector;

    // here are some operations on the vector

    throw std::runtime_error("I just want to throw!");

    contentVector.clear();
}

Будет ли линия contentVector.clear(); называться? Нет. Но вы по-прежнему в безопасности, потому что гарантированно будет вызван деструктор contentVector.

Из vector[2]:

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


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

[2] См. комментарии ниже, в которых содержатся некоторые мысли о документах SGI STL.

person Mark Garcia    schedule 05.02.2014
comment
SGI не является окончательной ссылкой. Просто говорю, потому что уверен, что в данном случае это правильно. - person Mark Ransom; 05.02.2014
comment
@MarkRansom Один ссылается на cppreference, другой на cplusplus.com, потом я... ;). Веб-сайт SGI STL, безусловно, является хорошим, если не исчерпывающим, ресурсом. ОП наверняка будет чему поучиться на этом классическом сайте. - person Mark Garcia; 05.02.2014
comment
Проблема в том, что SGI документирует не официальный стандарт, а его предшественника. Есть некоторые тонкие и не очень тонкие различия. См. stackoverflow.com/questions/5266386/ - person Mark Ransom; 05.02.2014
comment
@MarkRansom Я знаю. Но хорошо знать, что в этом случае она имеет такое же поведение, даже если она является предшественником стандартной библиотеки, а это означает, что вы все еще можете полагаться на поведение/гарантию для более старых компиляторов. - person Mark Garcia; 05.02.2014

Нет необходимости, он будет очищен автоматически, как только выйдет за рамки, т.е. деструктор уничтожит контейнерный объект.

person Varo    schedule 05.02.2014
comment
Можете ли вы предоставить ссылку на документацию STL, описывающую это поведение, и просто вставить ее в свой ответ? - person Michal Przybylowicz; 05.02.2014
comment
По общему мнению сообщества cplusplus.com не является хорошим справочником. - person Shafik Yaghmour; 05.02.2014

Вы можете не использовать функцию .clear(), потому что деструктор вектора запускается один раз contentVector выходит за рамки в «}».

Это освобождает память, в которой хранятся данные вектора.

person DaBuj    schedule 05.02.2014
comment
По общему мнению сообщества cplusplus.com не является хорошим справочником. - person Shafik Yaghmour; 05.02.2014

Поскольку я не думаю, что кто-то еще упоминал об этом, но если ваш вектор был

std::vector<std::string*> vector;

вы должны освободить память, выделенную для каждого элемента, до завершения функции (если только вы не передали право собственности на что-то еще, например, на глобальную переменную и т. д.)

for (auto i : vector) {
  delete i;
}
person Michael    schedule 05.02.2014
comment
В наши дни, когда С++ 11 становится довольно распространенным, вам, вероятно, следует использовать std::unique_ptr<std::string> вместо std::string*, чтобы механизм также позаботился об этом. И поскольку ваш цикл foreach указывает на то, что используется С++ 11, я бы пошел именно так. Кстати, это один из аспектов того, о чем говорится в сноске 1 в ответе Марка. - person MvG; 05.02.2014
comment
Авто часть писал потому что лень было выписывать полную версию без макросов vim. Если у вас С++ 11 или буст, то да. но деструктор указателей не освобождает память (и, очевидно, не может быть изменен) - person Michael; 05.02.2014