Мое настоящее понимание заключается в том, что и Операции перемещения и копирования C++ 11 Операторы должны вызывать удаление, чтобы предотвратить утечку памяти, , но что C++ 11 Перемещение и копирование Конструкторы должны не.C++ 11 Назначение переадресации и назначение копирования (операторы) - Использование удаления
Если мое понимание верное, конструкторам не нужно звонить delete
, однако я не знаю, почему. Рассмотрим следующий пример:
class my_class
{
my_class(int num_data) : my_data{new double[num_data]}, my_data_length{num_data}
{
}
// This class manages a resource
double *my_data;
int my_data_length;
}
// Big 4 go here - see wikipedia example below.
my_class a(10);
my_class b(10);
my_class c(10);
a = b; // Need to delete old storage contained in a before reallocating
a(c); // For some reason we don't need to delete the old storage here? I find this puzzling
Глядя на пример кода из этой wikipedia статьи, мне ясно, что:
Переместить Конструктор не попадала, потому что ресурсы передаются. Любые выделенные данные, на которые указывал класс , который равен , не истекает, переносится в истекающий класс и удаляется деструктором истекающего класса.
Я смущен относительно того, что утечка Копирования строится.
Оператор присваивания движения предположительно не течет, потому что он просто меняет указатели.
Я снова смущен Оператором присваивания копий. Я не уверен, почему требуется использование временного объекта? Я предполагаю, что ресурсы, принадлежащие
tmp
, иother
уничтожаются, когда они выходят за пределы области действия в конце этой функции? (За исключением тогоtmp
имеет свои ресурсы местами с указателем вthis
классе?)
Код от этого приводится ниже для удобства:
#include <cstring>
#include <iostream>
class Foo
{
public:
/** Default constructor */
Foo() :
data (new char[14])
{
std::strcpy(data, "Hello, World!");
}
/** Copy constructor */
Foo (const Foo& other) :
data (new char[std::strlen (other.data) + 1])
{
std::strcpy(data, other.data);
}
/** Move constructor */
Foo (Foo&& other) noexcept : /* noexcept needed to enable optimizations in containers */
data(other.data)
{
other.data = nullptr;
}
/** Destructor */
~Foo() noexcept /* explicitly specified destructors should be annotated noexcept as best-practice */
{
delete[] data;
}
/** Copy assignment operator */
Foo& operator= (const Foo& other)
{
Foo tmp(other); // re-use copy-constructor
*this = std::move(tmp); // re-use move-assignment
return *this;
}
/** Move assignment operator */
Foo& operator= (Foo&& other) noexcept
{
// simplified move-constructor that also protects against move-to-self.
std::swap(data, other.data); // repeat for all elements
return *this;
}
private:
friend std::ostream& operator<< (std::ostream& os, const Foo& foo)
{
os << foo.data;
return os;
}
char* data;
};
int main()
{
const Foo foo;
std::cout << foo << std::endl;
return 0;
}
Я предполагаю, что это намекает на то, почему это важно установить (неинициализированные/нераспределенные) висячие указатели на nullptr
, так как это предотвратит ошибки памяти в деструкторе при ее удалении?
Причина, по которой я думаю, что это связано с тем, что ресурсы передаются с помощью конструктора перемещения, но объект с истекающим сроком действия получает оборванный указатель, который никогда не был выделен, - мы не хотим, чтобы деструктор затем вызывался и delete
указатель - если мы не гарантируем, что он указывает на nullptr
(no-op).
Может кто-нибудь уточнить некоторые из пунктов, которые я поднял?