Понимание и использование конструктора назначения копирования - PullRequest
4 голосов
/ 08 июня 2019

Я пытаюсь понять, как работает конструктор присваивания копии в c ++.Я работал только с Java, так что я действительно из моих вод здесь.Я читал и видел, что это хорошая практика, чтобы вернуть ссылку, но я не понимаю, как я должен это сделать.Я написал эту небольшую программу для проверки концепции:

main.cpp:

#include <iostream>
#include "test.h"

using namespace std;

int main() {
    Test t1,t2;
    t1.setAge(10);
    t1.setId('a');
    t2.setAge(20);
    t2.setId('b');

    cout << "T2 (before) : " << t2.getAge() << t2.getID() << "\n";

    t2 = t1; // calls assignment operator, same as t2.operator=(t1)

    cout << "T2 (assignment operator called) : " << t2.getAge() << t2.getID() << "\n";

    Test t3 = t1; // copy constr, same as Test t3(t1)

    cout << "T3 (copy constructor using T1) : " << t3.getAge() << t3.getID() << "\n";

    return 1;
}

test.h:

class Test {
    int age;
    char id;

    public:
        Test(){};
        Test(const Test& t); // copy
        Test& operator=(const Test& obj); // copy assign
        ~Test();
        void setAge(int a);
        void setId(char i);
        int getAge() const {return age;};
        char getID() const {return id;};
};

test.cpp:

#include "test.h"

void Test::setAge(int a) {
    age = a;
}

void Test::setId(char i) {
    id = i;
}

Test::Test(const Test& t) {
    age = t.getAge();
    id = t.getID();
}

Test& Test::operator=(const Test& t) {

}

Test::~Test() {};

Кажется, я не понимаю, что мне следует положить внутрь operator=().Я видел, как люди возвращали *this, но то, что я прочитал, является просто ссылкой на сам объект (слева от =), верно?Затем я подумал о возвращении копии объекта const Test& t, но тогда не было бы смысла использовать этот конструктор, верно?Что я возвращаю и почему?

Ответы [ 3 ]

5 голосов
/ 08 июня 2019

Я читал и видел, что это хорошая практика - возвращать ссылку, но я не понимаю, как мне это сделать.

Как

Добавить

return *this;

в качестве последней строки в функции.

Test& Test::operator=(const Test& t) {
   ...
   return *this;
}

Почему

Что касается вопроса о том, почему вы должны возвращать *this, ответ таков: идиоматический.

Для фундаментальных типов вы можете использовать такие вещи, как:

int i;
i = 10;
i = someFunction();

Вы можете использовать их в цепочке операций.

int j = i = someFunction();

Вы можете использовать их в условных выражениях.

if ( (i = someFunction()) != 0 ) { /* Do something */ }

Вы можете использовать их при вызове функции.

foo((i = someFunction());

Они работают, потому что i = ... соответствует ссылке на i.Сохранять эту семантику идиоматично даже для пользовательских типов.Вы должны иметь возможность использовать:

Test a;
Test b;

b = a = someFunctionThatReturnsTest();

if ( (a = omeFunctionThatReturnsTest()).getAge() > 20 ) { /* Do something */ }

Но тогда

Что еще более важно, вам следует избегать написания деструктора, конструктора копирования и оператора назначения копирования для опубликованного класса.Созданных компилятором реализаций будет достаточно для Test.

2 голосов
/ 08 июня 2019

Мы возвращаем ссылку из оператора присваивания, чтобы мы могли сделать несколько интересных трюков, таких как @ SomeWittyUsername показывает .

Объект, на который мы хотим вернуть ссылку, - это тот, кто является оператором.вызывается или this.Итак, как вы уже слышали, вы захотите вернуть *this.

Так что ваш оператор присваивания, вероятно, будет выглядеть так:

Test& Test::operator=(const Test& t) {
    age = t.getAge();
    id = t.getID();
    return *this;
}

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

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

Это означает, что мы можем упростить ваш конструктор, просто используя оператор!

Test::Test(const Test& t) {
    *this = t;
}

Опять же, пока это работает дляВаш простой класс, в производственном коде с более сложными классами, мы обычно хотим использовать списки инициализации для наших конструкторов (подробнее читайте здесь ):

Test::Test(const Test& t) : age(t.getAge()), id(t.getId()) { }
2 голосов
/ 08 июня 2019

Возвращаемая ссылка на исходный объект необходима для поддержки вложенных операций.Рассмотрим

a = b = c
...