Подтвердить что ты не робот

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

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

Ответ 1

Проблема здесь в том, что вы нарушаете правило трех. В вашем классе есть деструктор, поэтому вам понадобится копировать-конструктор и оператор присваивания. Кроме того, вы не могли позволить копировать ваш класс (например, создав T(T const&) и T& operator=(T const&) private, или, получив 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 или С++ 11).

Ответ 2

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

Ответ 3

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

Ответ 4

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

Вы можете сделать следующее:

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

Ответ 5

Чтобы избежать разрушения временного и избежать конструкторов копирования, рассмотрите возможность использования vector:: resize или вектор:: emplace_back. Здесь пример с использованием emplace_back:

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

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

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