перегрузка оператора = изменяет исходный объект - PullRequest
0 голосов
/ 19 декабря 2018
struct List {
    int size;
    int* items;
    List& operator=(const List& l);
};

List& List::operator=(const List& l)
{
    delete[] items;
    size = l.size;
    items = new int[20];

    for (int i = 0; i < size; i++)
        items[i] = l.items[i];

    return *this;
}

ostream& operator<<(ostream& out, const List& l)
{
    out << "Size: " << l.size << "\t {";
    for (int i = 0; i < l.size; i++)
        out << l.items[i] << " ";

    return (out << "}");
}

int main()
{
    int size = 6;
    List l1 = { size, new int[size]{ 0, 1, 2, 3, 4, 5 } };
    List l2 = l1;

    l2.items[1] = 50;

    cout << l1 << endl;
    cout << l2 << endl;

    return 0;
}

Я в замешательстве, поскольку, когда я назначаю l2 = l1 с помощью перегруженного оператора, почему меняется содержимое l1 при изменении l2 позже?Тем более что l1 передается как const.Они как-то указывают на один и тот же объект в памяти вместо того, чтобы быть копией.

Ответы [ 3 ]

0 голосов
/ 19 декабря 2018

List l2 = l1; не вызывает оператор копирования (operator=(const List& l)).Поскольку «присваивание» происходит во время объявления переменной, вы вместо этого инициализируете l2 через инициализацию копирования, которая вызывает сгенерированный компилятором конструктор копирования по умолчанию.Поскольку он выполняет поверхностное копирование, оба объекта будут указывать на одни и те же данные.

Если вы собираетесь писать свои собственные классы, которые управляют памятью / ресурсами, вам необходимо по крайней мере предоставить свой собственный конструктор копирования, оператор назначения копированияи Destrcutor.Это известно как правило трех .Если вы хотите включить семантику перемещения, вам нужно предоставить конструктор перемещения и оператор присваивания перемещения, который известен как правило 5. Существует также правило нуля, где используются типы использования, которые уже "делают правильные вещи" (например, использование std::vector), позволяющее сгенерированным компилятором значениям по умолчанию работать на вас, и вы можете прочитать обо всем этом в Правило трех / пяти / нуля

0 голосов
/ 19 декабря 2018
List l2 = l1;

Несмотря на =, поскольку это объявление , вы выполняете построение копирования (формально «инициализация копирования»), которое не имеет ничего общего с оператором присваивания.

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

Результаты были бы такими, как вы ожидали, если бы вы написали:

List l2{};
l2 = l1;

Кстати, я бы дал значения по умолчанию size и items (0 и nullptr соответственно), если бы я был вами.В противном случае, когда вы забудете, что {}, члены имеют неопределенные значения, и все чертовски разрушаются.Это можно сделать с помощью хорошего конструктора по умолчанию или забыв обо всем этом предприятии и используя вместо него std::vector;)

0 голосов
/ 19 декабря 2018

List l2 = l1; вызывает сгенерированный компилятором конструктор копирования , который не выполняет глубокое копирование элемента указателя.

Если вы использовали std::vector для items, тогда вымог оставить конструкторы и оператор присваивания компилятору.(В вашем коде нет вызовов delete[], что означает, что ваш класс будет терять память, как дуршлаг пропускает воду.)

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