Почему мой класс деструктор вызывается, когда я добавляю экземпляры в вектор?
Кажется, что каждый раз, когда я добавляю объект в вектор 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());
}
Ответы
Ответ 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()
является необязательным, но обеспечивает доступность достаточного пространства до начала создания векторных элементов.