Нужна помощь в понимании вектора STL (простой код в теле сообщения) - PullRequest
0 голосов
/ 07 февраля 2010

Вот код:

#include <vector>
#include <iostream>

class A
{
public:
    A() { std::cout << __FUNCTION__ << "\n"; }
    ~A() { std::cout << __FUNCTION__ << "\n"; }

    A& operator=(const A&) { std::cout << __FUNCTION__ << "\n"; return *this;}
};

int main(int argc, char* argv[])
{
    std::vector<A> as;
    A a;
    as.push_back(a);
    as.push_back(a);
    return 0;
}

И вот что я получил:

A::A
A::~A
A::~A
A::~A
A::~A

Я понимаю, что вывод первой строки - это вызов c-tor с момента создания a Один из вызовов d-tor также принадлежит a. А как насчет трех других вызовов A :: ~ A (), откуда они? И почему звонков в d-tor больше, чем звонков в c-tor? Как контейнер клонирует «а», когда добавляет копии в свои элементы? И, наконец, определяется ли выходная реализация или есть другие возможные выходы?

Ответы [ 3 ]

3 голосов
/ 07 февраля 2010

Вам необходимо добавить конструктор копирования:

 A( const A& ) { std::cout << __FUNCTION__ << "\n"; }

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

2 голосов
/ 07 февраля 2010

Чтобы понять, что происходит, вам не хватает одного метода в A:

A(const A&) { std::cout << __FUNCTION__ << "(const A&)\n"; }

Тогда вы видите вывод:

A()
A(const A&)
A(const A&)
A(const A&)
~A
~A
~A
~A

Что происходит, так это то, что для каждого push_back вектор выделяет новый непрерывный массив, копирует старое содержимое и уничтожает его. Если считать, первый конструктор копирования предназначен для первого push_back, второй и третий для следующего push_back. Первый деструктор предназначен для второго push_back, два следующих для уничтожения вектора, последний для уничтожения переменной a.

И, между прочим, это полностью определяется реализацией, поскольку его можно распределять по чанкам, что предотвратит немало копий / уничтожений. Вы можете сделать это самостоятельно, используя vector::reserve(size_type n).

0 голосов
/ 07 февраля 2010

Поскольку я уверен, что вы понимаете, что вы создаете свои объекты "A" в стеке, и они затем копируются в массив (как предполагают другие ответы), неявно используя конструктор копирования.

Если ваши объекты A являются реальными объектами данных с собственными данными или сложным состоянием, которое трудно скопировать, вы можете рассмотреть вопрос о сохранении указателей в вашем массиве, а не о сохранении объектов напрямую. Тогда вам нужно будет самим управлять памятью, но об этом стоит подумать.

int main(int argc, char* argv[])
{
    std::vector<A*> as;
    A *a = new A();
    as.push_back(a);
    as.push_back(a);
    return 0;
}

Вы увидите вывод:

A()

Поскольку был создан только один экземпляр объекта, а массив просто хранит копии указателя . Очевидно, что в какой-то момент вам нужно будет удалить сам объект через один из его указателей, чтобы в конечном итоге вы вернули память.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...