Я думал, что понимаю как throw работает, но, похоже, это не так.
Такой значит вопрос:
#include <iostream.h>
class ce
{
char data[16];
public:
ce() { cout << "ce\t" << this << endl; };
ce(const ce &e) { cout << "cec\t" << this << endl; };
~ce() { cout << "~ce\t" << this << endl; };
};
main()
{
try
{ throw ce(); }
catch(ce &e)
{ cout << "catch\t" << &e << endl; }
}
---
Ничего удивительного нет:
ce 0x0012FF5C
catch 0x0012FF5C
~ce 0x0012FF5C
Теперь закомментируем не используемый конструктор копирования
// ce(const ce &e) { cout << "cec\t" << this << endl; };
И что же мы видим:
ce 0x0012FF4C
catch 0x0012FF5C
~ce 0x0012FF5C
То есть либо вызвался конструктор копирования по умолчанию, и забыл вызваться деструктор первого класса, либо класс самопроизвольно переместился в памяти. Либо я торможу.
P.S.
Может это нормально, когда классы сами ходят?
P.P.S.
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86
> нее > это не ты тормозишь, а Я тоже протормозил - я без ключа -GX cl.exe запускал.
> > Microsoft (R) 32-bit C/C++ Optimizing Compiler Version > > 12.00.8804 for 80x86 > :) Но это им не помогло:
ce 0x0012FF4C
~ce 0x0012FF4C
catch 0x0012FF5C
~ce 0x0012FF5C
Теперь уж ясно - до первого деструктора конструктор копирования по умолчанию вызвался.
Только не ясно: нафига он там? И почему его нет при явном задании его.
И что делать, если у меня, например, указатель где-то на этот класс...
E:\+>cl -GX c.cpp
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 12.00.8804 for 80x86
Copyright (C) Microsoft Corp 1984-1998. All rights reserved.
c.cpp
c.cpp(16) : warning C4508: 'main' : function should return a value; 'void' return type assumed
Microsoft (R) Incremental Linker Version 6.00.8447
Copyright (C) Microsoft Corp 1992-1998. All rights reserved.
/out:c.exe
c.obj
E:\+>c.exe
ce 0x0012FF5C
catch 0x0012FF5C
~ce 0x0012FF5C
хм...29.11.01 23:18 Автор: Biasha <Бяша> Статус: Member
> > А у меня все как-то буднично и предсказуемо... > > > > MSVC6 SP5 > Аналогично. > > > ce 0x0012FF5C > > catch 0x0012FF5C > > ~ce 0x0012FF5C > > Именно так и должно быть. > Причём в обоих случаях - при явно заданом конструкторе > копирования и при закоментированном.
Упс, я обманул - когда комментировал, не снял в соседнем окне файл с дебага, и он просто не слинковался. Сейчас перепроверил, картина совпадает с твоей.
Кстати, между catch(ce &e) и catch(ce e) разница такие есть - в последнем случае получается еще на одно копирование больше:
ce 0x0012FF40
~ce 0x0012FF40
catch 0x0012FF60
~ce 0x0012FF60
~ce 0x0012FF50
В принципе, понять, почему происходит копирование, можно. Объект создается на стеке, при выходе за границу блока должен быть удален - тем более, если этот throw случился не тут же, а в какой-нибудь функции f, вызванной из try. Тут как раз претензий нет. А вот почему при наличии копирующего конструктора копирование исчезает, действительно непонятно.
Оставалась небольшая надежда на то, что компилятор шибко умный и даже при отключенной оптимизации что-то там пытается соптимизировать, но ситуация повторилась и при разнесении на три файла класса ce, функции, возбуждающей исключение, и функции - обработчика.
Что забавно, добиться исключения копирования можно и без копирующего конструктора - достаточно добавить в класс любую виртуальную функцию, либо сделать деструктор виртуальным. Причем добавление любого другого констроктора не помогает.
Возможно, при таком минимальном усложнении класса объект-исключение начинает храниться уже не в стеке, а где-то еще.
Спасибо всем, кажеться, можно на этом закончить30.11.01 01:08 Автор: Biasha <Бяша> Статус: Member
Кроме как смириться с тем, что даже при catch(ce &e) может произойти копирование об'єкта, похоже делать нечего.
Ну и правильно - нечего писать класс без конструктора копирования. :)
> Именно так и должно быть. > Причём в обоих случаях - при явно заданом конструкторе > копирования и при закоментированном. не факт
никто этого не гарантирует
механизм исключений это всё-таки не просто вызов обработчика
ватком и борланд даже создают объект не в динамической памяти, а в одной из секций модуля
> Только не ясно: нафига он там? И почему его нет при явном > задании его. ... и почему не видно что он вызывается ? :)
> И что делать, если у меня, например, указатель где-то на > этот класс... указатели на временные объекты - это не хорошо
с указателем всё уже по другому будет
мне самому стало интересно - щас попробую, потом напишу чё получилось :)
Бывает явно заданный конструктор копирования, и конструктор копирования класса по умолчанию.
Конструктор копирования у класса есть всегда. Например, он вызывается дважды при вызывове функции типа CName func(CName name);.
К.к. по умолчанию - просто копирует все по очереди переменные-члены.
Чтобы не копировать об'єкт лишний раз, его передают по ссылке, как я и сделал (catch (ce &e)).
Так вот. Когда конструктор копирования задан явно - всё отлично, а когда явно он не задан (а значит при необходимости будет использован к.к. по умолчанию), то происходит копирование об'єкта куда-то. Само собою мы не видим на экране его вызова, так как в к.к по умолчанию нет вывода на экран текста, в отличие от нашего; мы можем понять, что он был вызван, только анализируя порядок вызова конструкторов/деструкторов, адреса об'єктов и содержимое переменных - всё указывает на наличие его вызова.
Вот и не понятно: чего это вдруг передаваемый по ссылке об'єкт понадобилось копировать. И почему этого не нужно делать при явно заданном к.к.
Да, было бы очень интересно другим компилятором попробовать. У меня нету тоже :(
Под С++Builder'ом 5 Interprise Suite получилось так29.11.01 13:15 Автор: Xan Статус: Незарегистрированный пользователь
> > Да, было бы очень интересно другим компилятором > > попробовать. У меня нету тоже :( > > ce 0012FF78 > cec 00795362 > ~ce 0012FF78 > catch 00795362 > ~ce 00795362 Зачем же вызвался cec... Ведь передача, кажется, по ссылке...
Если бы было catch(ce e), а не catch(ce &e), то так и должно было бы быть.
Может я ошибаюсь - нет разницы?
> Убираем явный вызов КК > ce 0012FF78 > ~ce 0012FF78 > catch 007953A2 > ~ce 007953A2 Это меня уже не удивляет...
Теперь совсем не понятно:
Почему MS в случае явно определенного конструктора копирования не создавал временный об'єкт, а borland создаёт...
Теперь я не понимаю совсем29.11.01 18:12 Автор: ggg <ggg> Статус: Elderman
> > ce 0012FF78 > > cec 00795362 > > ~ce 0012FF78 > > catch 00795362 > > ~ce 00795362 > Зачем же вызвался cec... Ведь передача, кажется, по > ссылке... > Если бы было catch(ce e), а не catch(ce &e), то так и > должно было бы быть. > Может я ошибаюсь - нет разницы?
как я понимаю в throw создаётся временный локальный объект
когда мы выходим из блока try он уничтожается
когда мы доходим до блока catch ссылка была бы уже не правильной
поэтому создаётся копия объекта для блока catch
> Теперь совсем не понятно: > Почему MS в случае явно определенного конструктора > копирования не создавал временный об'єкт, а borland > создаёт... наверно мс попытался оптимизировать это дело
но почему только в одном случае ?
вот что Watcom сказал29.11.01 18:24 Автор: ggg <ggg> Статус: Elderman
"Служебным является и слово catch. После него идет в скобках описание, которое используется аналогично описанию формальных параметров функции, а именно, в нем задается тип объектов, на которые рассчитан обработчик, и, возможно, имена параметров"
В функциях класс, переданый по ссылке не копируется.
Да и зачем его копировать.
Сас Страуструп у себя в примерах различает catch(ce &e) и catch(ce e). А тут выходит различия нет.