Правильный способ написания функций автоматического копирования и почему?C ++ - PullRequest
0 голосов
/ 23 февраля 2019

Я читаю книгу о c ++ и в разделе «Копирование» автор учит нас писать operator= в классе, который говорит нам, что мы должны быть уверены, что метод безопасен для самоопределения. когда у нас есть класс, который использует динамическую память.

Итак, представьте, что у нас есть класс с именем "Bank_Client" с std::string, созданный с помощью new.Книга научит нас делать это, чтобы избежать случая самоопределения:

Bank_Client& Bank_Client::operator=(const Bank_Client &addres){
    std::string *temp = new std::string(*addres.name);
    delete name;
    name = temp;
    return *this;
}

Так что если я сделаю

Bank_Client bob("Bobinsky");
bob = bob;

Программа не просто взорвется.Но именно тогда, когда я подумал, что переменная temp была пустой тратой времени, автор книги показал нам другой способ сделать это:

Bank_Client& Bank_Client::operator=(const Bank_Client &addres){
    if (this != &addres){
        delete name;
        name = new std::string(*addres.name);
    }
    return *this;
}

Как будто он читал мои мысли.НО сразу после этого он говорит нам никогда не делать этого, лучше сделать это другим путем, но никогда не объяснять почему.

Почему первый способ лучше?Это медленнее, не так ли?Что лучше для этого?

Как насчет использования assert, чтобы проверить, нет ли самостоятельного назначения?(потому что нам это действительно не нужно).А затем дезактивируйте его с помощью соответствующего NDEBUG, тогда проверка не будет пустой тратой времени.

Ответы [ 2 ]

0 голосов
/ 23 февраля 2019

Вы должны использовать копирование и обмен.Для этого вам нужен конструктор копирования (и, возможно, также конструктор перемещения, если вы хотите использовать семантику перемещения).

class Bank_Client {
    // Note that swap is a free function:
    // This is important to allow it to be used along with std::swp
    friend void swap(Bank_Client& c1, Bank_Client& c2) noexcept {
        using std::swap;
        swap(c1.name, c2.name);
        // ...
    }
    // ...
};

// Note that we took the argument by value, not const reference
Bank_Client& Bank_Client::operator=(Bank_Client address) {
    // Will call the swap function we defined above
    swap(*this, adress);
    return *this;
}

Теперь давайте посмотрим на код клиента:

Bank_Client bob("Bobinsky");
// 1. Copy constructor is called to construct the `address` parameter of `operator=()`
// 2. We swap that newly created copy with the current content of bob
// 3. operator=() returns, the temporary copy is destroyed, everything is cleaned up!
bob = bob;

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

Другое преимущество состоит в том, что вам нужна только одна реализация для operator=() для обработки семантики копирования и перемещения (копирование-и-замена и перемещение-и-замена).Если производительность - большая проблема, у вас все еще может быть перегрузка rvalue operator=(Bank_Client&&), чтобы избежать дополнительного движения (хотя я не одобряю это).

В качестве последнего слова, я бы также посоветовал вам попытаться положиться на правило 0 в меру ваших возможностей, как в примере выше, в случае изменения содержимого класса вы также должны соответствующим образом обновить функцию подкачки.

0 голосов
/ 23 февраля 2019

Первый способ медленнее , если объект самоназначается.Тем не менее, самостоятельное назначение редко .Во всех других случаях дополнительная if проверка со вторым подходом является пустой .

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

...