Почему деструктор моего класса вызывается при добавлении экземпляров в вектор?

Кажется, что каждый раз, когда я добавляю объект в вектор m_test, вызывается метод деструктора. Я что-то упускаю? Как я могу предотвратить это?

class TEST
{
public:
    TEST();
    ~TEST();
    int * x;
};

TEST::TEST()
{
}

TEST::~TEST()
{
... it is called every time I push_back something to the vector ...
    delete x;
}

    vector<TEST> m_test;
    for (unsigned int i=0; i<5; i++)
    {
        m_test.push_back(TEST());
    }

5 ответов


проблема здесь в том, что вы нарушаете правило трех. У вашего класса есть деструктор, поэтому вам также нужен конструктор копирования и оператор присваивания. Кроме того, вы не можете позволить копировать свой класс (например, сделав T(T const&) и T& operator=(T const&) частный, или путем выводить от boost::noncopyable), а затем измените размер вектора вместо использования push_back.

в первом случае, вы можете просто push_back ваш класс, как обычно. Во-вторых, синтаксис будет чем-то вроде

std::vector<TEST> vec(5);
// vec now has five default-constructed elements of type TEST.

не делать ни одну из этих вещей-плохая идея, так как вы, скорее всего, столкнетесь с проблемами двойного удаления в какой-то момент-даже если вы думаете, что никогда не будете копировать или назначать TEST здесь x != nullptr, гораздо безопаснее явно запретить это.

кстати, если у вас есть указатели членов, которые должны быть удалены, когда объект выходит за рамки, рассмотрите возможность использования интеллектуальных указателей, таких как scoped_ptr, unique_ptr и shared_ptr (и, возможно, auto_ptr если вы не можете использовать Boost или C++11).


это не называется когда вы push_back, это называется когда Временное разрушается.

чтобы исправить это в вашем примере:

TEST test;
for (int i = 0; i < 5; ++i)
{
    m_test.push_back(test);
}

следует вызвать его только один раз.

ваш код создает временный TEST внутри цикла, используя его в push_back, то это временное выходит за рамки, когда цикл заканчивается / повторяется и разрушается. Это происходит именно так, как должно, так как временное TEST должен чистить вверх.

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

vector<TEST> m_test(5); // Note reserving space in the vector for 5 objects

std::fill(m_test.begin(), m_test.end(), TEST()); // Fill the vector with the default ctor

в зависимости от того, как оптимизирован ваш STL, это может не потребоваться для создания нескольких копий.

вы также можете получить лучшую обработку, если вы реализуете конструктор копирования в своем TEST класс, как:

TEST::TEST(const TEST & other)
{
    x = new int(*other.x); // Not entirely safe, but the simplest copy ctor for this example.
}

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


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


на m_test.push_back(TEST());, TEST () создаст временную переменную. После копирования вектора в собственную память временная переменная уничтожается.

вы можете сделать так:

vector<TEST> m_test(5, TEST());

чтобы избежать уничтожения временного и чтобы избежать копирования конструкторов, рассмотрите возможность использования вектор::размер или вектор::emplace_back. Вот пример использования emplace_back:

vector<TEST> m_test;
m_test.reserve(5); 
for ( uint i=0; i<5; i++ ) 
{
    m_test.emplace_back();
}

вектор элемент будет построен на месте без необходимости копирования. Когда VT уничтожается, каждый векторный элемент автоматически уничтожается.

C++0x требуется (использовать -std=c++0x С gnu). #include <vector> конечно тоже требуемый.

если конструктор по умолчанию не используется (например, если TEST::x была ссылка вместо указателя), просто добавьте аргументы к вызову emplace_back() следующим образом:

class TEST
{
public:
    TEST( int & arg) : x(arg) {;} // no default constructor
    int & x; // reference instead of a pointer.
};

. . . 

int someInt;

vector<TEST> m_test;
m_test.reserve(5);
for ( uint i=0; i<5; i++ ) {
    m_test.emplace_back( someInt ); // TEST constructor args added here.
}

The reserve() shown является необязательным, но гарантирует, что перед началом построения векторных элементов доступно достаточное пространство.