Почему деструктор моего класса вызывается, когда я добавляю экземпляры в вектор? - PullRequest
13 голосов
/ 17 февраля 2012

Кажется, что каждый раз, когда я добавляю объект в вектор 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 ]

10 голосов
/ 17 февраля 2012

Проблема в том, что вы нарушаете Правило Трех .В вашем классе есть деструктор, поэтому вам также понадобится конструктор копирования и оператор присваивания.Кроме того, вы не можете разрешить копирование вашего класса (например, сделав 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).

7 голосов
/ 17 февраля 2012

Он не называется , когда вы 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).

1 голос
/ 17 февраля 2012

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

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

vector<TEST> m_test(5, TEST());
1 голос
/ 17 февраля 2012

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

0 голосов
/ 12 мая 2013

Чтобы избежать уничтожения временных и , чтобы избежать конструкторов копирования, рассмотрите возможность использования vector :: resize или vector :: 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.
}

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

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