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