Копировать конструктор заменить Переместить конструктор? - PullRequest
1 голос
/ 17 мая 2019

Я изучаю семантику перемещения из ссылки

У меня есть класс

class Holder
{
public:

    Holder(int size)         // Constructor
    {
        m_data = new int[size];
        m_size = size;
    }

    ~Holder()                // Destructor
    {
        delete[] m_data;
    }
    Holder(const Holder& other)
    {
        cout << "copy constructor" << endl;
        m_data = new int[other.m_size];
        memcpy(m_data, other.m_data, sizeof(int));
        m_size = other.m_size;
    }
    Holder &operator=(const Holder& other)
    {
        if (this == &other)
            return *this;
        delete[]m_data;
        m_data = new int[other.m_size];
        memcpy(m_data, other.m_data, sizeof(int));
        m_size = other.m_size;
        return *this;  
    }

private:

    int*   m_data;
    size_t m_size;
};

Этот класс имеет конструктор копирования, например:

    Holder(const Holder& other)
    {
        cout << "copy constructor" << endl;
        m_data = new int[other.m_size];
        memcpy(m_data, other.m_data, sizeof(int));
        m_size = other.m_size;
    }

А затем конструктор перемещения реализован так:

Holder(Holder&& other)     // <-- rvalue reference in input
{
  m_data = other.m_data;   // (1)
  m_size = other.m_size;
  other.m_data = nullptr;  // (2)
  other.m_size = 0;
}

У меня вопрос: почему мы не реализуем конструктор копирования, как показано ниже:

Holder( Holder& other)
{
    m_data = other.m_data;
    m_size = other.m_size;

    other.m_data = nullptr;
    other.m_size = 0;
}

Не могли бы вы показать мне, почему этот способ не используется? Спасибо

Ответы [ 3 ]

1 голос
/ 17 мая 2019

Ваша реализация этого «конструктора копирования» семантически представляет собой нечто среднее между конструктором копирования и конструктором перемещения, что может привести к путанице и стать опасным. Если ваше намерение состоит в том, чтобы перемещаться всегда и никогда не иметь возможности копировать объект, то вы можете принудительно заставить класс не копироваться следующим образом:

Holder(const Holder& other) = delete;
Holder& operator=( const Holder& ) = delete;

Даже если вы следуете правилам не генерации компилятором конструктора копирования по умолчанию и назначением по умолчанию, явное удаление этих методов более понятно.

1 голос
/ 17 мая 2019

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

В общем случае конструктор перемещения более полезен для использования с временными значениями, такими как:

Holder a_function(...){...}

Который затем может быть использован для строительства, как:

Holder object(a_function(...));

Или чтобы избежать переназначения / копирования большого объема данных / памяти при выполнении чего-то вроде:

Holder object(Holder(100));

Но специально для этого случая, в котором нет конструктора по умолчанию (поэтому, как правило, каждый объект должен быть заполнен после построения), имея конструктор копирования в соответствии с тем, что вы предложили (аналогично конструктору перемещения), а затем выполните:

Holder object1(100);
Holder object2(object1);

Можно было бы получить object1, который выглядит как обычный объект, но он пуст. Следовательно, он может стать источником ошибок / с позже.

Хотя это и очевидно, я должен добавить, что не будет проблем с уничтожением объекта1. Это просто то, что, используя его в течение срока его службы, незнание его пустоты, если не установлены какие-либо безопасные средства защиты (пограничные проверки), скорее всего, приведет к незаконному доступу к памяти.

0 голосов
/ 17 мая 2019

У меня вопрос: почему мы не реализуем конструктор копирования, как показано ниже :

Holder( Holder& other)
{
    m_data = other.m_data;
    m_size = other.m_size;

    other.m_data = nullptr;
    other.m_size = 0;
}

Не могли бы вы показать мне, почему этот способ не используется?

Приведенный выше код не копирует состояние из другого, а перемещает принадлежащие государству / находящиеся в собственности ресурсы другого в создаваемый текущий объект.

из Перемещение конструкторов

Перемещение конструкторов обычно "крадет" ресурсы, удерживаемые аргументом (например, указатели на динамически размещаемые объекты, файловые дескрипторы, TCP сокеты, потоки ввода / вывода, запущенные потоки и т. д.), а не делать копии из них, и оставить аргумент в каком-то действительном, но в противном случае неопределенное состояние.

Holder A;
Holder B(std::move(A));  
// B is created by calling move constructor    
// Resources held by A are transferred to B  (ref1)

Holder C;
Holder D(C);             
//C is created by calling copy constructor
//state or resources of C, D are same and C can be used after this
//Object C usable

ref1

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