Проблема с определением наследника
От: Hawk Россия  
Дата: 25.10.04 22:01
Оценка:
Следующий код:

struct Base
{
    virtual CString Who() const { return "base"; }
    virtual Base& operator << (int i) { return *this; }
};

struct Derived : public Base
{
    virtual CString Who() const { return "derived"; }
};


int _tmain(int argc, _TCHAR* argv[])
{
    try
    {
        throw Derived() << 1;
    }
    catch (const Base& b)
    {
        std::cout << b.Who() << "\n";
    }
 
   return 0;
}

выводит "base", хотя вроде как должен "derived". Если убрать вызов оператора <<:

try
{
    throw Derived() /*<< 1*/;
}
catch (const Base& b)
{
    std::cout << b.Who() << "\n";
}

код начинает работать как ожидалось (т.е. выводится "derived").

Почему? Каким тут боком замешана функция-оператор? И что можно сделать, что бы выводилось "derived", при условии, что оператор убирать нельзя и в catch должен остаться базовый тип?
Re: Проблема с определением наследника
От: jazzer Россия Skype: enerjazzer
Дата: 25.10.04 22:16
Оценка: 3 (1)
с catch проблем здесь нет, проблема в throw — исключение формируется из актуального типа выражения, а оно у тебя в случае с оператором — Base. Соответственно, объект исключения будет иметь тип Base и будет создан путем копирования (т.е. срезки) из твоего объекта Derived. В случае без оператора у тебя тип выражения — Derived, соответственно он и кидается.

Решение в данном случае — либо,если компилятор поддерживает ковариантные возвращаемые значения, в Derived добавить перекрытый оператор<<, возвращающий ссылку на Derived.

Второе решение — создавать объект динамически и кидать указатель на него, а в catch — убивать.
В этом случае не забудь про виртуальный деструктор (он 90% нужен, если ты занимаешься виртуальными функциями).
Но это — плохая практика, ибо, во-первых, исключение может быть поймано через catch(...), и тогда ты не сможешь очистить память, а во-вторых, операция выделения памяти сама может кинуть исключение и ловить и обрабатывать придется его вместо того, что тебе надо.

Так что перекрой оператор<< в Derived — это наилучшее решение.
Если компилятор позволит, конечно.
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re: Проблема с определением наследника
От: Bell Россия  
Дата: 26.10.04 06:17
Оценка: 5 (2)
Здравствуйте, Hawk, Вы писали:

Дело в том, что throw выбрасывает объект, созданный исходя из статического типа опреранда (15.1/3). В случае с operator << статический тип — это Base, поэтому throw создает объект типа Base, при этом исходный объект типа Derived срезается. Во втором случае выражение имеет тип Derived, поэтому и выбрасывается объект типа Derived.

Простейшее решение в данном случае — использовать явный каст:

throw static_cast<Derived&>(Derived() << 1);
Любите книгу — источник знаний (с) М.Горький
Re: Проблема с определением наследника
От: ScorpZ Украина  
Дата: 26.10.04 14:06
Оценка: +1
Здравствуйте, Hawk, Вы писали:

H>Следующий код:


H>
struct Base
H>{
H>    virtual CString Who() const { return "base"; }
H>    virtual Base& operator << (int i) { return *this; }
H>};

H>struct Derived : public Base
H>{
H>    virtual CString Who() const { return "derived"; }
H>};


H>int _tmain(int argc, _TCHAR* argv[])
H>{
H>    try
H>    {
H>        throw Derived() << 1;
H>    }
H>    catch (const Base& b)
H>    {
H>        std::cout << b.Who() << "\n";
H>    }
 
H>   return 0;
H>}

H>выводит "base", хотя вроде как должен "derived". Если убрать вызов оператора <<:

H>
try
H>{
H>    throw Derived() /*<< 1*/;
H>}
H>catch (const Base& b)
H>{
H>    std::cout << b.Who() << "\n";
H>}

H>код начинает работать как ожидалось (т.е. выводится "derived").

H>Почему? Каким тут боком замешана функция-оператор? И что можно сделать, что бы выводилось "derived", при условии, что оператор убирать нельзя и в catch должен остаться базовый тип?


Перкрыть реализацию operator << в классе Derived .
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.