Форум русскоязычного сообщества Ubuntu


Хотите сделать посильный вклад в развитие Ubuntu и русскоязычного сообщества?
Помогите нам с документацией!

Автор Тема: Странные конструкторы копий (C++)  (Прочитано 1616 раз)

0 Пользователей и 1 Гость просматривают эту тему.

Оффлайн Kernel ops

  • Автор темы
  • Старожил
  • *
  • Сообщений: 1388
    • Просмотр профиля
читаю книгу про C++, сейчас на конструкторах копий. пишу пример по прочитанному:
Код: (C++) [Выделить]
/* Создание конструктора копии */

using namespace std;

#include <iostream>

class test {
int *ch;
public:
test(int inNum) {ch=new int; *ch=inNum; cout << "Был вызван конструктор объекта (" << *ch << ")." << endl;};
test(const test &obj) {ch=new int; *ch=*obj.ch; cout << "Был вызван конструктор копии объекта (" << *ch << ")." << endl;};
~test() {cout << "Был вызван деструктор объекта (" << *ch << ")." << endl; delete ch;};
int getCh() {return *ch;};
};

test func() {
test a(8); // Вызов обычного конструктора
return a;
}

int main() {
test a(10); // Вызван обычный конструктор
test b=a; // Вызван конструктор копии

test c=func();

return 0;
}
в main() участок до test c=func(); работает как надо (вызывается сначала конструктор, а затем конструктор копии).
в строчке tesc c=func(); вроде бы конструктор копии должен вызываться два раза, но он не вызывается ни разу. еще одна странность - при таком коде
Код: (C++) [Выделить]
int main() {
test a(10); // Вызван обычный конструктор
test b=a; // Вызван конструктор копии

test c(9);
c=func();

return 0;
}
выдает сегфолт.
собственно вопросы: почему не вызываются конструкторы копии (может C++ такой навороченный, что он сразу из области памяти функции переносит на место будущего объекта c? но ведь сначала должно быть подсчитано правое выражение, а затем вызван конструктор копии для присвоения c) и почему происходит сегфолт?

Пользователь решил продолжить мысль 24 Мая 2013, 22:35:22:
при выходе из области видимости func() объект a не разрушается :(
« Последнее редактирование: 24 Мая 2013, 22:35:22 от Kernel ops »
root@shkiper:~$mount -t btrfs /dev/head1 /mnt && ls /mnt | grep brain | xargs -i sh /mnt/{}

Оффлайн quiet_readonly

  • Участник
  • *
  • Сообщений: 133
    • Просмотр профиля
Re: Странные конструкторы копий (C++)
« Ответ #1 : 24 Мая 2013, 22:59:01 »
Перегрузите оператор присвоения
test& operator =(const test &obj)

Оффлайн Kernel ops

  • Автор темы
  • Старожил
  • *
  • Сообщений: 1388
    • Просмотр профиля
Re: Странные конструкторы копий (C++)
« Ответ #2 : 25 Мая 2013, 15:47:35 »
quiet_readonly,
переработал:
Код: (C++) [Выделить]
/* Создание конструктора копии */

using namespace std;

#include <iostream>

class test {
int *ch;
public:
test(int inNum) {ch=new int; *ch=inNum; cout << "Был вызван конструктор объекта (" << *ch << ")." << endl;};
test(const test &obj) {ch=new int; *ch=*obj.ch; cout << "Был вызван конструктор копии объекта (" << *ch << ")." << endl;};
~test() {cout << "Был вызван деструктор объекта (" << *ch << ")." << endl; delete ch;};
test operator = (test op2);
int getCh() {return *ch;};
};

test func() {
        test a(9); // Вызов обычного конструктора
        return a;
}

test test::operator = (test op2) {
*ch=*(op2.ch);
return *this;
}

int main() {
test c(8);
c=func();
cout << "Объекты синхронизированы!" << endl;

cout << "Завершение программы..." << endl;

return 0;
}
здесь вроде все понятно, работает как надо
Был вызван конструктор объекта (8).
Был вызван конструктор объекта (9).
Был вызван конструктор копии объекта (9).
Был вызван деструктор объекта (9).
Был вызван деструктор объекта (9).
Объекты синхронизированы!
Завершение программы...
Был вызван деструктор объекта (9).
конструктор копии используется только один раз, потому что перегружен оператор "=".
но вопрос остается: в первом примере при выходе из области видимости func() объект a не разрушается с виду он вообще "перемещается" из одной области видимости в другую, при чем на место c. может это крутая оптимизация в g++?
ЗЫ почему при перегрузке оператора не запускается конструктор копии для создания op2?
« Последнее редактирование: 25 Мая 2013, 15:49:17 от Kernel ops »
root@shkiper:~$mount -t btrfs /dev/head1 /mnt && ls /mnt | grep brain | xargs -i sh /mnt/{}

Оффлайн kupamaan

  • Любитель
  • *
  • Сообщений: 52
    • Просмотр профиля
Re: Странные конструкторы копий (C++)
« Ответ #3 : 25 Мая 2013, 20:42:16 »
Цитировать
ЗЫ почему при перегрузке оператора не запускается конструктор копии для создания op2?
Надо немного подправить код.
(Нажмите, чтобы показать/скрыть)
Попробуйте.
« Последнее редактирование: 25 Мая 2013, 21:01:01 от kupamaan »

Оффлайн Kernel ops

  • Автор темы
  • Старожил
  • *
  • Сообщений: 1388
    • Просмотр профиля
Re: Странные конструкторы копий (C++)
« Ответ #4 : 25 Мая 2013, 22:53:20 »
kupamaan,
Вот весь код, который получился:
(Нажмите, чтобы показать/скрыть)
Вывод:
Был вызван конструктор объекта (2).
Был вызван конструктор копии объекта (2).
Был вызван конструктор объекта (9).
Объекты синхронизированы!
Завершение программы...
Был вызван деструктор объекта (9).
Был вызван деструктор объекта (2).
Был вызван деструктор объекта (2).
то есть конструктор копии вызывается только в "test b = c;"
в func() так-же не вызывается, хотя должен :(
ЗЫ как вообще организовывается совместимость конструктора копии с оператором "="?
root@shkiper:~$mount -t btrfs /dev/head1 /mnt && ls /mnt | grep brain | xargs -i sh /mnt/{}

Оффлайн kupamaan

  • Любитель
  • *
  • Сообщений: 52
    • Просмотр профиля
Re: Странные конструкторы копий (C++)
« Ответ #5 : 25 Мая 2013, 23:57:07 »
Цитировать
ЗЫ как вообще организовывается совместимость конструктора копии с оператором "="?

Bjarne Stroustrup:
Цитировать
Копирующий конструктор инициализирует 'сырую - ранее не иницализированую' память,
в то время как операция присваивания работает над участком памяти,содержащим корректно
сконструированный классовый объект.

по какой книге вы учитесь?

Цитировать
в func() так-же не вызывается, хотя должен


а с какой радости он должен вызываться в func()?





Оффлайн Kernel ops

  • Автор темы
  • Старожил
  • *
  • Сообщений: 1388
    • Просмотр профиля
Re: Странные конструкторы копий (C++)
« Ответ #6 : 26 Мая 2013, 01:03:37 »
kupamaan,
Шилдт Г. С++ Базовый курс (3-е издание, 2010)
пишет, что когда функция возвращает объект, для которого определен конструктор копии, неявно создается его копия по конструктору, которую возвращает return (если не определен - копируется побитно). вики сообщает то-же самое:
Цитировать
Существует четыре случая вызова конструктора копирования:
Когда объект возвращает значение
Когда объект передается (функции) по значению в качестве аргумента
Когда объект конструируется на основе другого объекта (того же класса)
Когда компилятор генерирует временный объект (как в первом и втором случаях выше; как явное преобразование и т.д.)
ЗЫ то есть "type a=b;" - инициализация, вызываем конструктор копии или побитно копируем; "type a; type b; a=b;" - присваивание, вызываем оператор присваивания или его перегруженный аналог?
root@shkiper:~$mount -t btrfs /dev/head1 /mnt && ls /mnt | grep brain | xargs -i sh /mnt/{}

Оффлайн kupamaan

  • Любитель
  • *
  • Сообщений: 52
    • Просмотр профиля
Re: Странные конструкторы копий (C++)
« Ответ #7 : 26 Мая 2013, 02:19:58 »
Цитировать
Был вызван конструктор объекта (9).
Объекты синхронизированы!
Завершение программы...
Был вызван деструктор объекта (9).

и чего вам ещё нужно?

Мне не понятно где вы его хотите ещё увидеть?

Оффлайн Kernel ops

  • Автор темы
  • Старожил
  • *
  • Сообщений: 1388
    • Просмотр профиля
Re: Странные конструкторы копий (C++)
« Ответ #8 : 26 Мая 2013, 02:23:16 »
test a = func();должен испоьзовать конструктор копии, так как func() возвращает объект

Пользователь решил продолжить мысль 26 Мая 2013, 14:30:41:
кажется я нашел объяснение 8)
вот один код:
Код: (C++) [Выделить]
/* Создание конструктора копии */
 
using namespace std;
 
#include <iostream>
 
class test {
        int x_;
public:
        test(int x) {x_=x; cout << "Был вызван конструктор объекта (" << x_ << ")." << endl;};
        test(const test &obj) {x_=obj.x_; cout << "Был вызван конструктор копии объекта (" << x_ << ")." << endl;};
        ~test() {cout << "Был вызван деструктор объекта (" << x_ << ")." << endl;};
        int getCh() {return x_;};
};
 
test func() {
        test a(9); // Вызов обычного конструктора

        return a;
}
 
 
int main() {
        test c(2);
        cout << "#CH1# " << endl;
c=func();
cout << "#CH2# " << endl;

        cout << "Объекты синхронизированы!" << endl;
       
        cout << "Завершение программы..." << endl;
       
        return 0;
}
для удобства я убрал new и delete, чтобы не выдавало сегфолт без перегрузки оператора =
здесь используется "оптимизация" g++ (текст вида #текст# добавлен как пояснение):
Был вызван конструктор объекта (2). #test c(2)#
#CH1# #cout << "#CH1# " << endl;#
Был вызван конструктор объекта (9). #test a(9);#
Был вызван деструктор объекта (9). #a вышла из области видимости, при этом сначала неявно скопирована в c (побитно)#
#CH2#
Объекты синхронизированы!
Завершение программы...
Был вызван деструктор объекта (9). #c выходит из области видимости main()#
другой код:
Код: (C++) [Выделить]
/* Создание конструктора копии */
 
using namespace std;
 
#include <iostream>
 
class test {
        int x_;
public:
        test(int x) {x_=x; cout << "Был вызван конструктор объекта (" << x_ << ")." << endl;};
        test(const test &obj) {x_=obj.x_; cout << "Был вызван конструктор копии объекта (" << x_ << ")." << endl;};
        ~test() {cout << "Был вызван деструктор объекта (" << x_ << ")." << endl;};
        int getCh() {return x_;};
};
 
test func() {
        test a(9); // Вызов обычного конструктора

        return a;
}
 
 
int main() {
        test c(2);
        cout << "#CH1# " << endl;
c=func();
cout << "#CH2# " << endl;

        cout << "Объекты синхронизированы!" << endl;
       
        cout << "Завершение программы..." << endl;
       
        return 0;
}
модифицируем функцию func():
Код: (C++) [Выделить]
test func() {
        test a(9), *p=&a; // Вызов обычного конструктора

        return *p;
}
вывод при такой модификации:
Был вызван конструктор объекта (2).
#CH1#
Был вызван конструктор объекта (9).
Был вызван конструктор копии объекта (9).
Был вызван деструктор объекта (9).
Был вызван деструктор объекта (9).
#CH2#
Объекты синхронизированы!
Завершение программы...
Был вызван деструктор объекта (9).
две добавленные строки означают, что на место func() подставляется временный объект, который создается конструктором копии от временного объекта, получившегося в результате вычисления *p. то есть здесь нет никакой оптимизации, идем "напролом"
итого, оптимизация появляется когда мы вызываем "return объект;" и исчезает, когда мы вызываем "return выражение;".
в одном из моих предыдущих сообщений применяется инициализация, она дает другой результат - rerurn a; "перемещает" результат в инициализируемую переменную (без деструктора).
cамое интересное - если в первом варианте использовать динамически-выделяемую память - произойдет сегфолт.
PS так это оптимизация или фича c++?
« Последнее редактирование: 26 Мая 2013, 14:30:41 от Kernel ops »
root@shkiper:~$mount -t btrfs /dev/head1 /mnt && ls /mnt | grep brain | xargs -i sh /mnt/{}

Оффлайн Grigory Smirnov

  • Старожил
  • *
  • Сообщений: 1339
  • Дайте мне исходники, и я переверну Землю.
    • Просмотр профиля
    • Дафтер
Re: Странные конструкторы копий (C++)
« Ответ #9 : 27 Мая 2013, 16:47:03 »
Kernel ops,
простите, что не по теме. А что за книга? Вариант электронный?

Оффлайн Kernel ops

  • Автор темы
  • Старожил
  • *
  • Сообщений: 1388
    • Просмотр профиля
Re: Странные конструкторы копий (C++)
« Ответ #10 : 27 Мая 2013, 21:18:42 »
Grigor7,
да, электронный. уже писал выше: "Шилдт Г. С++ Базовый курс (3-е издание, 2010)".
* Дальше слышен звук выбитой двери, крик бойцов спецназа и лязг наручников. Kernel ops так никто больше не увидел. Говорят, был  парень, споривший про какие-то конструкторы и C с двумя плюсами, да только пропал на сибирских рудниках *
root@shkiper:~$mount -t btrfs /dev/head1 /mnt && ls /mnt | grep brain | xargs -i sh /mnt/{}

 

Страница сгенерирована за 0.074 секунд. Запросов: 25.