Конструктор копирования + мелкая и глубокая копия - PullRequest
1 голос
/ 02 августа 2020

Я хотел спросить, когда я не пишу конструктор копирования явно, компилятор автоматически генерирует конструктор копирования, который по умолчанию выполняет мелкое копирование, верно? Итак, в программе main (), когда я изменил значения целых чисел a, b и указателя p, изменилось только значение p, а значения a и b остались неизменными в скопированном объекте. Почему не изменились и значения a и b? Мой код:

#include <iostream>
#include <string.h>
using namespace std;

class Dummy {
    private:

        int a, b;
        int *p;
    public:
        Dummy() {
            p = new int;
        }
        void setData(int x, int y, int z) {
            a = x;
            b = y;
            *p = z;

        }
        void showData() {
            cout << "a = " << a << " b = " << b;
            cout << " p = " << *p << endl;
        }

        ~Dummy() {
            delete p;
        }
};

int main() {

    Dummy d1;
    d1.setData(3, 4, 5);
    Dummy d2 = d1;
    d1.showData();
    d2.showData();
    d1.setData(6, 7, 8);
    d1.showData();
    d2.showData();
    return 0;
}

Результат моей программы:

a = 3 b = 4 p = 5
a = 3 b = 4 p = 5
a = 6 b = 7 p = 8
a = 3 b = 4 p = 8

Я говорю, что указатель объекта d2 изменился, когда я изменил значения объекта d1 тогда почему значения a и b объекта d2 тоже не изменились?

Также я использую ключевое слово delete в деструкторе для удаления динамически выделяемого указателя:

~Dummy() {
            delete p;
        }

Но вместо этого происходит сбой моей программы. Почему?

1 Ответ

2 голосов
/ 02 августа 2020

Вы совершенно ошиблись - The idea of shallow copy. На самом деле, c++ не имеет ничего, что называется deep copy, встроенного в себя. Итак, вызов чего-то shallow copy - это a bit wrong. И просто использование этих слов shallow copy также создает множество confusion.

Теперь позвольте мне объяснить, что происходит, когда cpp выполняет initialization using assignment. cpp или c (при копировании структуры) имеет концепцию, называемую bitwise copy. В этой концепции all the member variables of one object(struct object/class object - you can say either) is identically copied to another object. Теперь это totally wrong idea, both objects point to same memory location. Фактически, both object имеет свои own memory location и, конечно же, their variables занимает different memory spaces. Для вас я написал несколько тестов на память. Вы бы прекрасно поняли, если бы вы просто увидели тест и его результат:

#include <iostream>
#include <string.h>

using namespace std;


class Dummy {
    int a, b;
    int *p;
public:
    Dummy() {
        p = new int;
    }
    void setData(int x, int y, int z) {
        a = x;
        b = y;
        *p = z;
    }
    void showData() {
        cout << "a = " << a << " b = " << b;
        cout << " p = " << *p << endl;
        cout << endl; // an extra new line for readability of output
    }
    void showMemory() {
        cout << "addr(a) = " << &a << " addr(b) = " << &b;
        cout << " addr(p) = " << &p << endl;
    }
    ~Dummy() {
        *p = 100;
        delete p;
    }
};
// testing memory
void memoryTest() {
    cout << "testing d1:" << endl;

    Dummy d1;
    d1.setData(3, 4, 5);
    
    cout << "addr(d1) = " << &d1 << endl;
    d1.showMemory();
    cout << endl ;


    cout << "testing d2:" << endl;

    Dummy d2 = d1;
    cout << "addr(d2) = " << &d2 << endl;
    d2.showMemory();
}

int main() {
    // memoryTest
    memoryTest();

    return 0;
}

А результат теста был:

testing d1:
addr(d1) = 0x6dfed4
addr(a) = 0x6dfed4 addr(b) = 0x6dfed8 addr(p) = 0x6dfedc

testing d2:
addr(d2) = 0x6dfec8
addr(a) = 0x6dfec8 addr(b) = 0x6dfecc addr(p) = 0x6dfed0

Это ясно показывает, что память, занятая эти два объекта d1 и d2 совершенно разные.

Теперь у вас может остаться еще один вопрос: Тогда почему, когда я пишу *p=8, это влияет как на d1, так и на d2? :

Когда вы назначаете Dummy d2 = d1;, мы можем сказать, что произошло что-то, как показано ниже (хотя на самом деле этого не происходит, когда применяется побитовое копирование, это просто для ясности):

d2.p = d1.p

Итак, мы знаем, что d1.p и d2.p содержат одну и ту же ячейку памяти (примечание: d1.p является указателем. Поэтому он не содержит целого числа, а содержит адрес памяти типа int).

Итак, когда вы напишите *p = 8, вы сообщаете программе go в ячейку памяти, на которую указывает p, и изменяете значение этой ячейки памяти на 8. (обратите внимание, здесь вы не меняли содержимое d1.p, d1.p по-прежнему содержит ту же ячейку памяти. Скорее, вы просто изменили содержимое этой ячейки памяти с 5 на 8). Вот почему при вызове d2.p вы получаете измененное значение. причина, d2.p содержит ту же ячейку памяти, что и d1.p.

Теперь может возникнуть еще один вопрос: Почему ваш код вылетает при освобождении p в деструкторе? :

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

Ну, в Dummy destructor вы написали delete p;. Теперь сначала будет уничтожено либо d2, либо d1. Предположим, d2 уничтожено first. Итак, когда вызывается d2's разрушитель, p это freed. Затем будет вызван d1's разрушитель, который также попытается free p. Но p уже освобожден. И в вашем случае программа встречает cra sh по этой причине.

Надеюсь, теперь вам все ясно.

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

...