Вопросы по памяти поведения векторов

В последнее время я немного запутался в распределении памяти (де) std::vectors

Предположим, я получил нормальный вектор целого числа:std::vector<int> intv; Когда я push_back немного intЭто растет со временем. И когда я покидаю область действия (то есть) функции, она освобождается без необходимости дополнительных вызовов.

Отлично. Давайте рассмотрим другой пример:

struct foo_t{
    std::string bar:
    unsigned int derp;
}
void hurr(){
    std::vector<foo_t> foov;
    foo_t foo;
    foo.bar = "Sup?";
    foo.derp = 1337;
    foov.push_back(foo);
}

Хорошо. Когда я звоню hurr() вектор создается, foo_t экземпляр создается, экземпляр заполняется и помещается в вектор. Поэтому, когда я покидаю функцию, вектор освобождается и содержимое (здесь один foo_t) освобождается тоже?

Следующий пример:

struct foo_t{
    std::string bar:
    unsigned int derp;
}
std::vector<foo_t> hurr(){
    std::vector<foo_t> foov;
    foo_t foo;
    foo.bar = "Sup?";
    foo.derp = 1337;
    foov.push_back(foo);
    return foov;
}

В моем понимании, вектор и его содержимое живут в стеке, который (со временем) перезаписывается временем, а вектор, который я возвратил, и его содержимое будут бесполезны. Или он на самом деле возвращает копию вектора с копией его содержимого (требуется конструктор Copy для типа данных содержимого, если это не POD)?

И что-то очевидное:

struct foo_t{
    std::string bar:
    unsigned int derp;
}
std::vector<foo_t*> hurr(){
    std::vector<foo_t*> foov;
    foo_t foo = new foo_t;
    foo->bar = "Sup?";
    foo->derp = 1337;
    foov.push_back(foo);
    return foov;
}

Теперь я должен вручную перебрать вектор, удалить его содержимое, и тогда я смогу безопасно позволить вектору выпасть из области видимости, верно?

3 ответа

Решение

Этот пример:

struct foo_t{
    std::string bar;
    unsigned int derp;
};
void hurr(){
    std::vector<foo_t> foov;
    foo_t foo;
    foo.bar = "Sup?";
    foo.derp = 1337;
    foov.push_back(foo);
}

После hurv() законченный, foov а также foo оба освобождены.

std::vector<foo_t> hurr(){
    std::vector<foo_t> foov;
    foo_t foo;
    foo.bar = "Sup?";
    foo.derp = 1337;
    foov.push_back(foo);
    return foov;
}

результат std::vector<foo_t> из hurr() действует с 1 foo_t в этом и оно действительно. return foov; может вызвать копию конструктора std::vector<foo_t>, и он имеет право не делать эту копию, см. elision

В любом случае, из C++11 вы можете написать это:

struct foo_t{
    std::string bar;
    unsigned int derp;
    // we will copy the string anyway, pass-by-value
    foo_t(std::string bar_, unsigned int d_)
        : bar(std::move(bar_)), derp(d_) {}
};
std::vector<foo_t> hurr(){
    std::vector<foo_t> foov;
    // This is better, in place construction, no temporary
    foov.emplace_back("Sup?", 1337);
    // This require a temporary
    foov.push_back(foo_t{"Sup?", 1337});
    return foov;
}

И, в последнем примере, да, вы должны вручную выполнить итерации по вектору, удалить его содержимое, а затем я могу смело позволить, чтобы вектор выпал из области видимости, когда вы больше не хотите использовать результат hurr(), (не в hurr())

foov.push_back(foo);

На самом деле, вы построили foo_v и вы оттолкнули его назад, что на самом деле создал новый foo_v и вызвал конструктор копирования с foov в качестве показателя. использование emplace_back если ты хочешь избежать этого.

return foov;

Компилятор может оптимизировать это с помощью оптимизации возвращаемого значения. Посмотрите на эту короткую программу, которую я сделал на coliru, в качестве примера. Обратитесь к другим отличным ответам в этом вопросе.

std::vector<foo_t*> foov;
/* add elements to foov with new */

Теперь я должен вручную перебрать вектор, удалить его содержимое, и тогда я смогу безопасно позволить вектору выпасть из области видимости, верно?

Да, вы делаете. По тем же причинам

int* a = new int();

Не буду delete a; когда a умирает.

Поэтому, когда я покидаю функцию, вектор освобождается и содержимое (здесь один foo_t) освобождается тоже?

Да. И если foo_t был нетривиальный деструктор, это будет называться.

Или же он на самом деле возвращает копию вектора с копией его содержимого (требуется конструктор Copy для типа данных содержимого, если это не POD)?

Да, в этом случае он возвращает копию. Современные компиляторы могут вызывать конструктор копирования для std::vector, который, в свою очередь, будет вызывать конструктор копирования содержащегося типа для каждого элемента. C++17 вводит гарантированную оптимизацию возвращаемого значения (RVO), поэтому конструктор копирования вашего вектора не будет вызываться. Тем не менее, если вы установите высокий уровень оптимизации, современный компилятор также может использовать RVO.

Теперь я должен вручную перебрать вектор, удалить его содержимое, и тогда я смогу безопасно позволить вектору выпасть из области видимости, верно?

Да ты прав. Подумайте об использовании умных указателей, если вы не хотите выполнять итерации вручную.

Другие вопросы по тегам