В чем секрет конструктора копирования.

Модераторы: Hawk, Romeo, Absurd, DeeJayC, WinMain

Dragon
Сообщения: 99
Зарегистрирован: 01 окт 2009, 11:21
Откуда: Odessa
Контактная информация:

Добрался до конструкторов копирования, но никак не могу осмыслить происходящее.
Может кто-нибудь схематично (на ячейках памяти) или может есть уже такая схема, объяснить как он работает?
Для разбора можно взять следующий пример:

Код: Выделить всё

class StringVar
{
   public:
   ~StringVar();
    StringVar(char a[]);
    StringVar(const StringVar& object);

   private:
      char *c;
      int max_length;
};

StringVar::StringVar(char a[]) : max_length(strlen(a))
{
   c = new char[max_length + 1]; //+1 for '\0'
   strcpy(c, a);
}

StringVar::StringVar(const StringVar& object) : max_length(object.length())
{   
    c = new char[max_length + 1]; //+1 for '\0'
    strcpy(c, object.c);
}

//=======
//Конструктор копирования
StringVar str("word");
StringVar temp(str);
Почему без передачи параметра по ссылке деструктор пытается очистить содержимое ячейки по одному адресу дважды? А при передаче параметра по ссылке (или все же взятие адреса?!) создает копию объекта в другой ячейке (по другому адресу)?

Расписал с адресами, как я это вижу:

Код: Выделить всё

StringVar str("word");
|---> line.c = 0x33ff77

StringVar temp(str);
|---> temp.c = 0x11aa33

В соответствии с конструктором копирования мы передаем аргумент object (он же str) по значению.
В итоге: strcpy(0x33ff77, 0x11aa33).
Если передавать аргумент по ссылке...
object.c получит копию адреса str.c или temp.c или вобще что-то другое ???

P.S> чувствую, что близок к пониманию, осталась вот эта маленькая стеночка, которую не могу преодолеть.
BulldozerBSG
Сообщения: 270
Зарегистрирован: 09 янв 2010, 04:14
Контактная информация:

А где код деструктора? В вопросе он упоминается.

length = object.length(); - А этоработает? Скобочки лишние...
Dragon
Сообщения: 99
Зарегистрирован: 01 окт 2009, 11:21
Откуда: Odessa
Контактная информация:

Код деструктора:
StringVar::~StringVar()
{
delete[] c;
}
max_length = object.length(); - А этоработает? Скобочки лишние...
Работает. Забыл добавить, в классе есть функция-член:

Код: Выделить всё

int StringVar::length() const
{
   return strlen(c);
}
Код взят из книги, рассматриваются указатели в классах и как-раз конструктор копирования, но как-то мутно описано, что не могу до конца понять "что будет, если передать параметр по значению", почему после деструктора очистятся оба объекта?
Albor
Сообщения: 491
Зарегистрирован: 06 сен 2004, 13:34
Откуда: Днепропетровск

Dragon писал(а):Код деструктора:
Работает. Уже перевел в более читабельный вид.

Аж интересно!!! Метод length() в классе не определён, допускаю, что просто не вставлен в листинг. Но как компилятор мирится с одинаковыми именами переменной и метода? А конструктор копирования без ссылки на String и компилироваться не должен (д.б. ошибка "illegal copy constructor: first parameter must not be a 'String'"), не то что до деструктора добраться. Подумай, если допустить конструктор с параметром Тип (String и данном случае), то конструктор, как и любая функция, должен скопировать данный параметр, причём вызвав точно такой-же конструктор, а тот, в свою очередь, сделает то же самое и так пока не закочится память.
Dragon
Сообщения: 99
Зарегистрирован: 01 окт 2009, 11:21
Откуда: Odessa
Контактная информация:

Имя класса String было взято от балды. Т.к. суть в понимании механики работы. Пусть класс будет называться Stroka, StringVar, VarString и т.д.
Albor
Сообщения: 491
Зарегистрирован: 06 сен 2004, 13:34
Откуда: Днепропетровск

Dragon писал(а):Имя класса String было взято от балды. Т.к. суть в понимании механики работы. Пусть класс будет называться Stroka, StringVar, VarString и т.д.
Так не в имени класса дело, а в переменной и методе length и в утверждении
Работает.
BulldozerBSG
Сообщения: 270
Зарегистрирован: 09 янв 2010, 04:14
Контактная информация:

Ув. Dragon имя метода не может совпадать с именем поля, это первое.
Второе. Вы не сможете описать с вашей точки зрения конструктор копирования в котором параметр не ссылка на объект такого же класса. Компилятор посчитает его другим конструктором а не конструктором копирования, а как следствие создаст свой конструктор копирования, т.к. это часть правильного функционирования ООП в С++.

Код: Выделить всё

String::String(char a[]) : length(strlen(a))
{
   c = new char[length + 1]; //+1 for '\0'
   strcpy(c, a);
}

String::String(const String& object) : length(object.length)
{  
    c = new char[length + 1]; //+1 for '\0'
    strcpy(c, object.c);
}

//=======
//Конструктор копирования
String var("word");
String temp(var);
И в данном коде при создании temp, object в конструкторе копирования совпадет с внешним var.
Dragon
Сообщения: 99
Зарегистрирован: 01 окт 2009, 11:21
Откуда: Odessa
Контактная информация:

Так не в имени класса дело, а в переменной и методе length и в утверждении
Ув. Dragon имя метода не может совпадать с именем поля, это первое.
Я знаю, что имя метода не может совпадать с именем поля - это вроде как одни из первых правил :)
Но погрешности в переделке примера из листинга для большей удобочитаемости (исправил, раз настаиваете).
Второе. Вы не сможете описать с вашей точки зрения конструктор копирования в котором параметр не ссылка на объект такого же класса. Компилятор посчитает его другим конструктором а не конструктором копирования, а как следствие создаст свой конструктор копирования, т.к. это часть правильного функционирования ООП в С++.
Я знаю, что конструктор копирования создается ИМЕННО так и никак иначе. Вопрос не в том, как компилятор будет себя вести, поймет он что это конструктор копирования или нет. Вопрос в том КАК работает конструктор копирования, ПОЧЕМУ указатели вызываемого и передаваемого объектов будут указывать на одну ячейку памяти, при передаче параметра по значению. Чисто схематически.

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

Я расписал на бумажке ячейки памяти, зависимости, но прийти к тому, почему в указателях адрес на одну и ту же ячейку не могу. Конечно это можно принимать как должное, что "так есть и работать оно должно вот так то". Знаю людей который используют быструю сортировку, а в реальности как этот детод работает не знают, им хватает того, что скорость O(n log n). Но мне для полного понимания такие мелочи важны, вот такой вот я педантичный, что поделаешь.
BulldozerBSG
Сообщения: 270
Зарегистрирован: 09 янв 2010, 04:14
Контактная информация:

Я знаю, что конструктор копирования создается ИМЕННО так и никак иначе. Вопрос не в том, как компилятор будет себя вести, поймет он что это конструктор копирования или нет. Вопрос в том КАК работает конструктор копирования, ПОЧЕМУ указатели вызываемого и передаваемого объектов будут указывать на одну ячейку памяти, при передаче параметра по значению.
А где это вы такое вычитали что при передачи параметра по значению указатели будут указывать на одну ячейку памяти? это будут разные адреса, т.к. это разные объекты хоть и одного типа.
L.A.V.
Сообщения: 20
Зарегистрирован: 16 авг 2009, 23:37
Откуда: Солнечный Крым
Контактная информация:

Dragon писал(а): ПОЧЕМУ указатели вызываемого и передаваемого объектов будут указывать на одну ячейку памяти, при передаче параметра по значению. Чисто схематически.
Потому что, как было было написано ранее, если ты передаешь как объект то:
BulldozerBSG писал(а): Компилятор посчитает его другим конструктором а не конструктором копирования, а как следствие создаст свой конструктор копирования, т.к. это часть правильного функционирования ООП в С++.
А работа стандартного конструктора копирования, созданного транслятором, не пыльная:
побитно скопировать значения полей(переменных) класса(структуры). Таким образом значение, что хранилось указателе *с (т.е. адрес, именно адрес, а не строка) благополучно копируется в такой же указатель, но уже в другом объекте.
Но когда приходит пора освобождения памяти, будь то завершение программы к примеру, то соответственно уничтожить программа должны оба объекта(при том при все что под второй объект мы память не выделяли).
так как при уничтожении одного объекта память уже по нашему "блудному" адресу почистилась, то когда приходит пора удалять второй объект - возникает ошибка: попытка два раза почистить одну и ту же область памяти.
Dragon писал(а): Почему без передачи параметра по ссылке деструктор пытается очистить содержимое ячейки по одному адресу дважды? А при передаче параметра по ссылке (или все же взятие адреса?!) создает копию объекта в другой ячейке (по другому адресу)?

Так на первый вопрос ответ какой ни какой есть. Теперь второй.
При передаче значения по ссылке срабатывает твой самописный конструктор копирования. В нем с помощью оператора new выделяется новый блок памяти, причем выделяется совершенно в другом месте отличном от исходного, соответственно адрес будет другой. При вызове деструкторов память чистится каждая по своему адресу. Все довольны и компилятор и программист. :)
Ответить