Форум программистов, компьютерный форум, киберфорум
Наши страницы

С++ для начинающих

Войти
Регистрация
Восстановить пароль
 
 
Рейтинг: Рейтинг темы: голосов - 30, средняя оценка - 4.83
kravam
быдлокодер
1701 / 888 / 45
Регистрация: 04.06.2008
Сообщений: 5,498
#1

Почему не вызывается конструктор копирования? - C++

05.11.2010, 21:40. Просмотров 4026. Ответов 53
Метки нет (Все метки)

...У меня в книге описаны случаи при которых вызывается конструктор копирования. Один из этих случаев:

: "Когда аргумент имеет тип класса. Создаётся копия аргумента и затем передаётся в функцию"

Ну-с попробуем смоделировать такую ситуацию:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <iostream>
using namespace std;
 
class baze {
public:
 
 //Конструктр объекта класса baze, заданный по умолчанию
 baze () {printf ("wwwww\n");};
 
 //Эта функция, которая принимает ссыочный аргумент. Следовательно, при её вызове ДОЛЖЕН 
 //вызваться конструктр копирования
 void funktsia(const baze &other){
  printf ("lllll\n");
 }
 
 //А вот и сам конструктр копирования. Он иногда должен вызываться. Где- см. ниже 
 baze (const baze &src) {
  printf ("rrrrr\n");
 }
 
};
 
int main() {
 
 baze f;
 
 //В это месте должен вызываться конструктор копирования, ибо функция funktsia() принимает
 //ссылочный аргумент. Тычем на исполнение и ждём появления строчки "rrrrr"
 f.funktsia(f);
 
 //Но наблюдаем всего лишь "wwwww" (создался объект класса baze) и "lllll" (вызвана функция
 //funktsia () Как она приняла аргумент- для меня до сих пор загадка.)
 //Конструктор копирования не вызван. ПОчему?
 getchar ();
 return 0;
}
Добавлено через 2 минуты
Спасибо заранее
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
05.11.2010, 21:40
Здравствуйте! Я подобрал для вас темы с ответами на вопрос Почему не вызывается конструктор копирования? (C++):

Почему не вызывается конструктор копирования при возврате объекта из функции - C++
Добрый день! Насколько мне известно, конструктор копирования вызывается: 1) при передачи объекта как аргумента функции ...

Когда вызывается конструктор копирования? - C++
Пожалуйста помогите ответить на вопрос &quot;Когда вызывается конструктор копирования?&quot;

Сколько раз вызывается конструктор копирования - C++
Почему n не увеличивается? Point(const Point&amp; src){ n++; set(src.x+1, src.y+1); cout &lt;&lt; &quot;Konstruction copyrovanija...

В каких случаях вызывается конструктор копирования, и стоит ли вообще об этом думать? - C++
В книге Г. Шилдта написано, что конструктор копирования может неявно вызываться, при инициализации объекта значением, которое возращает...

Будет ли утрачена память, когда конструктор копирования вызывается для уже существующего объекта класса? - C++
class A { char * v; A(); ~A(); A(const A &amp;obj); } ///////////////////// A::A() {

Почему не вызывается конструктор копии? - C++
Почему не вызывается конструктор копии? class CPoint { friend std::istream &amp;operator&gt;&gt;(std::istream &amp;in, CPoint &amp;obj); friend...

53
kravam
быдлокодер
1701 / 888 / 45
Регистрация: 04.06.2008
Сообщений: 5,498
12.05.2011, 10:14  [ТС] #16
Цитата Сообщение от SashaPinsk Посмотреть сообщение
Конструктор копирования в твоём коде вызывается. Почему ты решил, что он не вызывается?
потому, что я не вижу этой строки; проверено.
C++
1
cout << "Вызов копирующего конструктора" << "\n";
Также он не вызывается если:
C++
1
arr x= get_object(15);
, то есть x создаётся корректно, (для объекта arr я вводил приватный член и в конструкторе
присваивал ему значение
аргумента (в данном случае 15), а потом выводил его; выводилось без
ошибок)

Добавлено через 2 минуты
Цитата Сообщение от Deviaphan Посмотреть сообщение
Не путайте теорию с практикой. В книге написано одно, а оптимизирующий компилятор делает другое. Если можно безболезненно удрать копирование, оно будет убрано. Если можно вызов функции убрать, она будет убрана.
Ну, короче, всё плохо. Ты пыхтишь, переопределяешь конструкторы, ждёшь их вызов. А они не вызываются. И вся программа крахом.
...И даже если ты её подгонишь по капризы отдельного компилятора, под все компиляторы её не подогнать.
0
Deviaphan
Делаю внезапно и красиво
Эксперт С++
1306 / 1221 / 50
Регистрация: 22.03.2011
Сообщений: 3,744
12.05.2011, 10:47 #17
Цитата Сообщение от kravam Посмотреть сообщение
Ну, короче, всё плохо.
Нет! Всё хорошо!
В 99.9% случаев, конструктор копирования создаёт точную копию копируемого объекта. Оптимизирующий компилятор убирает создание временного объекта, оставляя результирующий, т.е. ты получаешь код с точно тем же функционалом, но без создания временного объекта. Т.е. меньше и по размеру и по времени выполнения. В ситуациях, когда конструктор копирования должен быть вызван, он будет вызван. Не переживай.

Добавлено через 3 минуты
В данном коде конструктор копирования будет вызываться. Компилятор ничего не поделает, т.к. у разных объектов должны быть разные адреса.
C++
1
2
3
4
arr A(5);
arr B( A );
// Используем А и В, чтобы их не удалил оптимизатор
cout << &A << &B;
0
kravam
быдлокодер
1701 / 888 / 45
Регистрация: 04.06.2008
Сообщений: 5,498
12.05.2011, 11:04  [ТС] #18
Речь не об экономии ресурсов на создании временного объекта- этот мне как раз понятно, что да, экономия. Но! Я переопределяю конструктор копирования, внедряю туда ВАЖНЫЙ код, а компилятор не вызывает его и код не выполняется.

А это болезненно. И даже очень болезненно. И даже очень-очень болезненно.
0
Deviaphan
Делаю внезапно и красиво
Эксперт С++
1306 / 1221 / 50
Регистрация: 22.03.2011
Сообщений: 3,744
12.05.2011, 11:28 #19
Цитата Сообщение от kravam Посмотреть сообщение
А это болезненно. И даже очень болезненно. И даже очень-очень болезненно.
Ты не прав. Компилятору понятно, что ты не прав, а тебе нет.)
Копирование - создание дубликата. Т.е. между ручным созданием двух одинаковых объектов и копированием второго из первого нет никакой разницы (в 99.9% случаев, но у тебя не тот 0.1%).
Компилятор видит, что первый объект создаётся только для инициализации второго, поэтому он его удаляет и инициализирует сразу второй объект. Всё корректно.
Если тебе уж очень хочется совпадения с теорией, то отключи в настройках проекта все оптимизации. А лучше в дебаге играйся, потрейсить сможешь.

Или сделай глобальную переменную и инкрементируй её значение в конструкторах. Возможно, компилятор не догадается, как такой код оптимизировать.
1
kravam
быдлокодер
1701 / 888 / 45
Регистрация: 04.06.2008
Сообщений: 5,498
12.05.2011, 12:15  [ТС] #20
ОК, а тогда для чего вообще придумана такая штука, как создание собственных конструкторов копирования? Если встроенный с успехом справляется с возложенной на него задачей? Вот одна из ситуаций:если один из членов объекта X будет указывать допустим, на строку. И вот: я пишу конструктор копирования, который создаёт объект Y с указателем на новую строку с тем же содержанием, что и в копируемом объекте (применяю посимвольное копирование строк).

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include <iostream>
#include <locale>
 
using namespace std;
class arr {
  public:
    arr(){p= new char [7]; strcpy (p, "stroka");};
    arr(const arr &obj);
    ~arr(){delete p;}
  private:
    char* p;          
};
 
 
//Вот в этом конструкторе строка копируется посимвольно
arr::arr(const arr &obj){
 p= new char [7];
 strcpy (p, obj.p);
 cout << "Вызов копирующего конструктора" << "\n";
}
 
 
//А вот по возвращении из этой функции и должен будет ызваться МОЙ конструктор копирования
//Но он не вызовется  
arr get_object () {
 arr obj;
 return obj; 
}
 
 
 
 
int main(){
 
  arr x= get_object();
  //А теперь мы имеем, что x.p указывает на строку, КОТОРОЙ НЕТ, ибо
  //для указателя, который на неё указывал, была выполнена операция:
  //delete p (по выходе из функции get_object обюъект obj был разрушен и был вызван его деструктор)         
  
  getchar ();
  return 0;
}
Всё, приехали. И такая ситуация как раз очень-очень часта; в том учебнике, где будет описано написание конструкторов копирования, считай, что там будет описана такая же ситуация. И это не 1%, согласись?
...И при её возникновении будет крах. То ли компилятор вставит то ли нет консруктор копирования.
А по опциям- у меня все опции отключены; да и простая компиляция из командной строки

C++
1
g++ main.cpp
приводит к результату невызывания моего конструктора копирования.

Добавлено через 3 минуты
Вообще любой АВТОРСКИЙ конструктор копирования (что бы он не делал) подразумевает отход от просто посимвольного копирования байтов, а некоторый СВОЙ код, которой не выполнится.
0
Deviaphan
Делаю внезапно и красиво
Эксперт С++
1306 / 1221 / 50
Регистрация: 22.03.2011
Сообщений: 3,744
12.05.2011, 12:22 #21
Повторяю, в твоём коде нет смысла создавать временный объект. Вместо создания "arr obj" с последующим копированием в "arr x", сразу же создаётся "arr x". Т.е. не происходит побитового копирования, создаётся всего один объект и ничего копировать не нужно.
Я привёл тебе варианты, когда одним объектом обойтись нельзя и копирование будет происходить.
Почитай про оптимизации, которые делает твой компилятор, ты будешь оооочень удивлён. Я серьёзно. Он ведь и количество аргументов у функций менять может и порядок операций менять, много чего может. И всё это не сказывается на результате. Так же, как на результате не сказывается удаление промежуточного объекта.
0
kravam
быдлокодер
1701 / 888 / 45
Регистрация: 04.06.2008
Сообщений: 5,498
12.05.2011, 12:38  [ТС] #22
Цитата Сообщение от Deviaphan Посмотреть сообщение
Повторяю, в твоём коде нет смысла создавать временный объект. Вместо создания "arr obj" с последующим копированием в "arr x", сразу же создаётся "arr x".
Неправда, давай проверим, вот сюда добавим строчку:

C++
1
2
3
4
5
6
7
//А вот по возвращении из этой функции и должен будет ызваться МОЙ конструктор копирования
//Но он не вызовется  
arr get_object () {
 cout << "Вызов функции" << "\n";
 arr obj;
 return obj; 
}
Угу? Ну ты согласишься, что если эта функция вызовется, то
всё-таки obj создастся?
И не будет скопирован (как я хочу посимвольно),
и будет уничтожен
и будет вызван его деструктор?

Добавлено через 7 минут
В конце концов, чё это я, надо было сразу так написать:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include <iostream>
#include <locale>
 
using namespace std;
class arr {
  public:
    arr(){p= new char [7]; strcpy (p, "stroka");};
    arr(const arr &obj);
    ~arr(){delete p;}
 
    char* get_p(){return p;}
 
  private:
    char* p;          
};
 
 
//Вот в этом конструкторе строка копируется посимвольно
arr::arr(const arr &obj){
 p= new char [7];
 strcpy (p, obj.p);
 cout << "Вызов копирующего конструктора" << "\n";
}
 
 
//А вот по возвращении из этой функции и должен будет ызваться МОЙ конструктор копирования
//Но он не вызовется  
arr get_object () {
 cout << "Вызов функции" << "\n";
 arr obj;
 printf ("obj.p =%x\n", obj.get_p());
 return obj; 
}
 
 
 
 
int main(){
 
  arr x= get_object();
  printf ("x.p =%x\n", x.get_p());
  //А теперь мы имеем, что x.p указывает на строку, которой нет, ибо
  //для указателя, котрый на неё указывал, была выполнена операция:
  //delete p (по выходе из функции get_object обюъект obj был разрушен и был вызван её деструктор)         
  
  getchar ();
  return 0;
}
Вот, запусти и увидишь, что указатели указывают на один и тот же участок памяти. Уже удалённый, а нового нет, ибо МОЙ конструктор вызван не был.
И эта ситуация часта.
0
Deviaphan
Делаю внезапно и красиво
Эксперт С++
1306 / 1221 / 50
Регистрация: 22.03.2011
Сообщений: 3,744
12.05.2011, 12:39 #23
Функция вызовется, да. Но вывод в консоль никак не связан с объектов obj. Я это вижу, компилятор это видит. И он может его здесь не создавать и не копировать. У тебя слишком простой пример, который компилятор слишком хорошо оптимизирует.
Ты пойми, весь ненужный код оптимизатор по возможности убирает, а тут копирование излишне.
0
kravam
быдлокодер
1701 / 888 / 45
Регистрация: 04.06.2008
Сообщений: 5,498
12.05.2011, 12:41  [ТС] #24
Ну я там добавил, посмотри
...Да я уже понял, что копилятор не вызовет мой конструктор. Я благодарен ему и восхищаюсь его умению самому принимать решения.
...Но лучше бы он этого не делал, ибо результат- указывание указателя на несуществующую строку. А это посерьёзнее чем просто невывод диагностической надписи "вызван конструктор копирования". Это полная ж...а
0
Deviaphan
Делаю внезапно и красиво
Эксперт С++
1306 / 1221 / 50
Регистрация: 22.03.2011
Сообщений: 3,744
12.05.2011, 12:42 #25
Цитата Сообщение от kravam Посмотреть сообщение
указатели указывают на один и тот же участок памяти. Уже удалённый, а нового нет
Ибо ТВОЙ конструктор копирования не вызывался, т.к. использовался объект из строки 40. Т.е. он был создан ДО вызова функции get_object и, затем, в ней настраивался.
Я уже писал, отключи оптимизации.
0
CheshireCat
Эксперт С++
2895 / 1244 / 78
Регистрация: 27.05.2008
Сообщений: 3,397
12.05.2011, 12:49 #26
Цитата Сообщение от kravam Посмотреть сообщение
... а теперь вот другая ситуация; тоже должен вызываться конструктор копирования и тоже он не вызывается. Почему? Хотя в книге написано, что конструктор копирования вызывается, когда возвращаемое значение имеет тип класса!
В книге написано правильно. Теоретически, конструктор копирования должен быть вызван. Но разработчики компилятор - хииитрые! Они использовали положение Стандарта языка, разрешающее т.н. Return Value Optimization - RVO - т.е. создавать возвращаемый объект obj прямо в том месте, "куда" он возвращается. В этом случае - да, конструктор копирования не будет вызван, что экономит процессорное время и улучшает производительность программы. Возможно, автор книги либо не мог знать об RVO (если книга писалась в давние-давние времена....), либо пока сознательно не стал заострять на этом внимание.
2
Deviaphan
Делаю внезапно и красиво
Эксперт С++
1306 / 1221 / 50
Регистрация: 22.03.2011
Сообщений: 3,744
12.05.2011, 12:58 #27
Кроме того, оператор присваивания не обязан работать через конструктор копирования. И кроме момента создания объекта, он не будет заменён на конструктор копирования.

Добавлено через 2 минуты
Кстати, в MSDN есть пример про удаление промежуточного объекта:
C++
1
2
3
4
5
6
A MyMethod (B &var)
{
   A retVal;
   retVal.member = var.value + bar(var);
   return retVal;
}
Ничего не напоминает? .)
0
kravam
быдлокодер
1701 / 888 / 45
Регистрация: 04.06.2008
Сообщений: 5,498
12.05.2011, 13:36  [ТС] #28
Цитата Сообщение от Deviaphan Посмотреть сообщение
Ибо ТВОЙ конструктор копирования не вызывался
Если это шутка, то неудачная, я битый час тебе об этом говорю уже.

Цитата Сообщение от Deviaphan Посмотреть сообщение
Я уже писал, отключи оптимизации.
как их отключать-то если они отключены? См. приложение
0
Вложения
Тип файла: rar optsii.rar (7.2 Кб, 10 просмотров)
kravam
быдлокодер
1701 / 888 / 45
Регистрация: 04.06.2008
Сообщений: 5,498
12.05.2011, 13:39  [ТС] #29
Цитата Сообщение от Deviaphan Посмотреть сообщение
Кроме того, оператор присваивания не обязан работать через конструктор копирования.
стоп, если ты про ЭТОТ вызов

C++
1
2
arr y;
arr x=y// ЭТО всё равно, что arr x(y)
Давай с ЭТИМ вызовом повременим, а то я запутаюсь вообще. Щас говорим о
C++
1
return x;
0
Deviaphan
Делаю внезапно и красиво
Эксперт С++
1306 / 1221 / 50
Регистрация: 22.03.2011
Сообщений: 3,744
12.05.2011, 14:01 #30
http://msdn.microsoft.com/en-us/library/ms364057%28v=vs.80%29.aspx
Уж не знаю, как объяснить проще, чем там написано. Своими словами не удалось, попробую ихними.
Все претензии к Майкрософту, что они удаляют ненужный код.)

Добавлено через 1 минуту
Цитата Сообщение от kravam Посмотреть сообщение
я битый час тебе об этом говорю уже.
читай до конца
Цитата Сообщение от Deviaphan Посмотреть сообщение
Ибо ТВОЙ конструктор копирования не вызывался, т.к. использовался объект из строки 40. Т.е. он был создан ДО вызова функции get_object и, затем, в ней настраивался.
Добавлено через 1 минуту
Значит, этот код настолько очевиден для компилятора, что он даже при выключенных оптимизациях от него избавляется.) Запусти под дебагом, там точно всё будет, как тебе хочется.
0
12.05.2011, 14:01
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
12.05.2011, 14:01
Привет! Вот еще темы с ответами:

Почему не вызывается конструктор перемещения? - C++
#include &lt;iostream&gt; #include &lt;vector&gt; class Object { public: Object() { std::cout &lt;&lt; &quot;Object()&quot; &lt;&lt; std::endl; ...

Почему вызывается конструктор, а не создание массива? - C++
struct KTest { KTest(int _a, double _b, long long _c) : a(_a), b(_b), c(_c) {} friend ostream&amp; operator &lt;&lt; (ostream&amp; os, KTest const&amp;...

Почему конструктор вызывается повторно при преобразовании типов? - C++
В качестве примера предлагаю рассмотреть примерчик из учебника. В примере мы создаём класс, содержащий в качестве поля строку символов и...

Почему конструктор вызывается при присвоении объекта другому объекту - C++
оператор+ выполняется нормально, но когда уже переменная в sum, на след шаге она вызывает конструктор, ибо у меня в примере 9 выходит. А в...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
30
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2017, vBulletin Solutions, Inc.
Рейтинг@Mail.ru