Винаги ли трябва да извиквам 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 в действие и както Bjarne Stroustrup казва в Защо 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 ще бъде извикан.

От вектор[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
Тези дни, когато C++11 става сравнително често срещан, вероятно трябва да използвате std::unique_ptr<std::string> вместо std::string*, така че машините да се погрижат и за това. И тъй като вашият цикъл foreach показва, че се използва C++11, това е начинът, по който бих тръгнал. Между другото, това е един аспект от това, за което се говори в тази бележка под линия 1 в отговора на Марк. - person MvG; 05.02.2014
comment
Написах авточастта, защото ме мързеше да напиша пълната версия без макроси на vim. Ако имате c++11 или boost, тогава да. но деструкторът на указатели не освобождава паметта (и очевидно не може да бъде променен) - person Michael; 05.02.2014