2017-02-08 39 views
1

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

Вопрос в том, в чем заключается преимущество определения конструктора копирования, как в классе CopyCat, в моем примере, если я получаю тот же результат с пустым конструктором копии (как в классе EmptyCat)?

Мой второй вопрос: почему класс Cat и класс EmptyCat работают по-другому? Единственное различие между ними заключается в том, что я определяю пустой конструктор копирования в EmptyCat. Но когда я запускаю программу, я вижу, что в EmptyCat после копирования элемент указателя указывает на новое местоположение, а в классе Cat - на мелкой копии.

#include "iostream" 

class Cat 
{ 
    public: 
    void GetMem() { std::cout << itsAge << "\n"; } 
    private: 
    int * itsAge = new int; 
}; 

class EmptyCat 
{ 
    public: 
    EmptyCat() {} 
    ~EmptyCat() {} 
    EmptyCat(EmptyCat&obj) {} 
    void GetMem() { std::cout << itsAge << "\n"; } 
    private: 
    int * itsAge = new int; 
}; 

class CopyCat 
{ 
    public: 
    CopyCat() {} 
    ~CopyCat() {} 
    CopyCat(CopyCat&obj); 
    int GetAge() { return *itsAge; } 
    void GetMem() { std::cout << itsAge << "\n"; } 
    private: 
    int * itsAge = new int; 
}; 

CopyCat::CopyCat(CopyCat & obj) 
{ 
    itsAge = new int; 
    *itsAge = obj.GetAge(); 
} 

int main() 
{ 
    Cat Garfield; 
    Cat Kitty(Garfield); 

    std::cout << "Memory addresses for the objects' <itsAge> member:" << std::endl; 
    std::cout << "Garfield and Kitty (Class Cat):" << std::endl; 
    Garfield.GetMem(); 
    Kitty.GetMem(); 

    EmptyCat Meow; 
    EmptyCat Purr(Meow); 

    std::cout << std::endl << "Meow and Purr (Class EmptyCat):" << std::endl; 
    Meow.GetMem(); 
    Purr.GetMem(); 

    CopyCat Fluffy; 
    CopyCat Felix(Fluffy); 

    std::cout << std::endl << "Fluffy and Felix (Class CopyCat):" << std::endl; 
    Fluffy.GetMem(); 
    Felix.GetMem(); 

    system("pause"); 
    return 0; 
} 

При запуске программы я получаю это:

Memory addresses for the objects' <itsAge> member: 
Garfield and Kitty (Class Cat): 
00BBDA60 
00BBDA60 

Meow and Purr (Class EmptyCat): 
00BB46A0 
00BB8280 

Fluffy and Felix (Class CopyCat): 
00BB82B0 
00BBE8A0 
Press any key to continue . . . 
+3

Ваш класс утечки памяти, поэтому это не очень хороший пример чего-либо. –

+0

Я хотел сделать это как можно проще, сосредоточившись на поведении конструктора копий – SVG

+0

Но при этом вам не хватает всего интересного. Если ваш деструктор должен был удалить выделение, вы сразу обнаружите правило из трех и все такое. –

ответ

0

Мой вопрос, что является преимуществом определения конструктора копирования, как и я в классе CopyCat в моем примере, если я получаю тот же результат с пустым конструктором копирования (как в классе EmptyCat)?

Вы не получите тот же результат. CopyCat выделяет новую память и копирует значение из старого класса. EmptyCat просто выделяет новую память, но не копирует значение.

Мой второй вопрос: почему класс Cat и класс EmptyCat работают по-другому? Единственное различие между ними заключается в том, что я определяю пустой конструктор копирования в EmptyCat. Но когда я запускаю программу, я вижу, что в EmptyCat после копирования элемент указателя указывает на новое местоположение, а в классе Cat - на мелкой копии.

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

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

int * itsAge = new int; 

Это то, что выделяет новый int и получает вас другое значение указателя.

+0

Основываясь на вашем ответе, я внесла некоторые изменения, и мне удалось представить, как работает ограниченный определенный конструктор копий. Конструктор копирования с пустым определением фактически не является конструктором копирования, он ничего не копирует, он фактически работает как создание нового объекта. Он создает новый объект, основанный на определении класса, и поэтому я запутался, потому что в определении класса был введен оператор 'int * itsAge = new int;' в определении класса (поэтому на основе этого пустого конструктора копирования выделена новая память для новый объект). – SVG

+0

Я сделал несколько дальнейших исследований, и я выяснил, что так называемый неявный конструктор копирования (который используется, когда нет объявленного конструктора копирования) эквивалентен этому: 'Класс :: Класс (const Класс и объект): x (object.x), y (object.y), z (object.z) {} ' Таким образом, неявный конструктор копий делает больше, чем определенно определенный. – SVG

0

Вы не получаете такое же поведение с и без пустого конструктора копирования. EmptyCat(EmptyCat& obj) { } ничего не делает.

CopyCat(CopyCat& obj) { 
    itsAge = new int; 
    *itsAge = obj.GetAge(); 
} 

динамически выделяет новый int и присваивает ему значение от obj.

0

Глубокое копирование и мелкое копирование - это скорее концепция C, где у вас есть только структуры и необработанные указатели. Указатель может принадлежать, и в этом случае копия должна быть глубокой или может быть разделена, и в этом случае копия неглубока (и вы должны быть осторожны, чтобы освободить ее, если она выделена с помощью malloc).

В C++ новое теперь эффективно устарело. У нас есть уникальные указатели, которые являются «владельцами указателей» и «общих указателей». Однако указатели относительно редки. Элементами классов классов являются std :: vectors, членами строки являются std :: strings. И копии автоматически глубокие (вы используете ссылку, если хотите мелкую копию).

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