Not what I expected from some exception code (throw/try/catch)

Discussion in 'C++' started by stevewilliams2004, Jul 16, 2007.

  1. I was wondering if someone could explain the output I am getting for
    the program below. What I expected from the main program output was
    "Cat" but instead I see "Mammal". The output is also included below.
    I got the same results with GCC 3.4.4 under cygwin as with MSDev
    studio 2003. Even stranger to me, if I change the catch statement to
    catch a Cat instead of a Mammal, the program crashes in the catch
    body, during the call to m.MyType(). Thanks for any explanations in
    advance.

    Program:

    #include <iostream>
    using namespace std;

    class Mammal
    {
    public:
    Mammal()
    {
    cout << "Constructing Mammal @ " << this << endl;
    }
    Mammal(const Mammal& source)
    {
    cout << "Copy Constructing Mammal @ " << this << " from " <<
    &source << endl;
    }
    ~Mammal()
    {
    cout << "Destructing Mammal @ " << this << endl;
    }
    virtual const char* MyType()
    {
    return "Mammal";
    }
    };
    class Cat : public Mammal
    {
    public:
    Cat()
    {
    cout << "Constructing Cat @ " << this << endl;
    }
    Cat(const Cat& source)
    {
    cout << "Copy Constructing Cat @ " << this << " from " <<
    &source << endl;
    }
    ~Cat()
    {
    cout << "Destructing Cat @ " << this << endl;
    }

    virtual const char* MyType()
    {
    return "Cat";
    }
    };

    int main(int argc, char *argv[])
    {
    Cat fluffy;
    Mammal &fluffyRef = fluffy;
    try
    {
    throw fluffyRef;
    }
    catch (Mammal &m)
    {
    cout << "Caught a " << m.MyType() << endl;
    return 0;
    }
    cout << "Nothing Caught" << endl;
    return 0;
    }

    Output:

    [email protected] /cygdrive/f/dev/src/TestWorkspace
    $ ./TestWorkspace.exe
    Constructing Mammal @ 0x22ccc0
    Constructing Cat @ 0x22ccc0
    Copy Constructing Mammal @ 0xc306d8 from 0x22ccc0
    Caught a Mammal
    Destructing Mammal @ 0xc306d8
    Destructing Cat @ 0x22ccc0
    Destructing Mammal @ 0x22ccc0
     
    stevewilliams2004, Jul 16, 2007
    #1
    1. Advertisements

  2. * :
    It would, yes, because the exception is then not caught and propagates
    out of "main".

    This I believe is a hypothesis of yours, and an incorrect one.

    Formally, this copy constructs a Mammal to some Mammal instance (the
    exception object) provided by the runtime support.

    C++ "throw" is by value.

    If you don't intend to modify the object, catch by reference to const.
    That way you document your intentions, in the source code. Which is
    generally the best place to document such low-level details.

    Hth.,

    - Alf
     
    Alf P. Steinbach, Jul 16, 2007
    #2
    1. Advertisements

  3. stevewilliams2004

    Kai-Uwe Bux Guest

    You throw a Mamal. At this point, a temporary Mamal is created and
    initialized from fluffyRef, which involves slicing. See [15.1/3]:

    A throw-expression initializes a temporary object, called the exception
    object, the type of which is determined by removing any top-level
    cv-qualifiers from the static type of the operand of throw and adjusting
    the type from ?array of T? or ?function returning T? to ?pointer to T?
    or ?pointer to function returning T?, respectively.

    Note that the _static type_ is used for the temporary.

    Also note that a temporary copy is needed since the original is a local
    object that has to go down during stack unwinding.

    At this point, the temporary implicitly created in the throw() is used to
    initialize the Mamal& here. Since slicing already happened, you don't get a
    Cat.


    Best

    Kai-Uwe Bux
     
    Kai-Uwe Bux, Jul 16, 2007
    #3
  4. Nope - I verified this with a debugger. The MSDev debugger crashed on
    the call m.MyType(), and in cygwin I got the cout just prior to the
    call and not the one after it.

    Also - looking with the MSDev debugger is shows that the VTable for
    the m object is invalid (i.e. "Expression cannot be evaluated" or some
    such method). The exception is *not* propagating out of main.
     
    stevewilliams2004, Jul 16, 2007
    #4
  5. Ok I think I am understanding now - and my last post was not entirely,
    correct - I see now. Thanks for your quick responses.
     
    stevewilliams2004, Jul 16, 2007
    #5
  6. * :
    Could theoreticalle happen because you have Undefined Behavior where
    anything can happen (in principle), but my guess is you debugged
    something that wasn't what you described.
     
    Alf P. Steinbach, Jul 16, 2007
    #6
  7. stevewilliams2004

    BobR Guest

    class Mammal{ public:
    Mammal(){
    cout<<"Constructing Mammal @ "<<this<<std::endl;
    }
    Mammal( Mammal const &source ){
    cout<<"Copy Constructing Mammal @ "<<this
    <<" from "<<&source<<std::endl;
    }

    // ~Mammal(){
    // [Warning] class Mammal' has virtual functions but
    // non-virtual destructor
    // [Warning]`class Cat' has virtual functions but
    // non-virtual destructor

    virtual ~Mammal(){ // this quiets both warnings
    cout<<"Destructing Mammal @ "<<this<<std::endl;
    }
    virtual const char* MyType(){
    return "Mammal";
    }
    };
    // no change to Cat
    class Cat : public Mammal{ public:
    Cat(){
    cout<<"Constructing Cat @ "<<this<<std::endl;
    }
    Cat( Cat const &source ){
    cout<<"Copy Constructing Cat @ "<<this
    <<" from "<<&source<<std::endl;
    }
    ~Cat(){
    cout<<"Destructing Cat @ "<<this<<std::endl;
    }
    virtual const char* MyType(){
    return "Cat";
    }
    };
    throw fluffyRef; // to get 'Mammal' (a sliced 'Cat')
    // out: Copy Constructing Mammal @ 0xc5fb80
    // from 0xacee04 (Cat fluffy)

    // throw fluffy; // to get 'Cat'
    // Copy Constructing Cat @ 0xc5fb80 from 0xacee04
    // add:
    catch( Cat const &m ){
    cout<<"Caught a "<<m.MyType()<<std::endl;
    return EXIT_FAILURE;
    }
    // add:
    catch( ... ){ // that's 3 dots (not a placeholder)
    cout<<"Caught something, maybe the flu!"<<std::endl;
    return EXIT_FAILURE;
    }
    [ I'm not positive, this is my guess. ]
    Your 'fluffyRef' is a ref to 'Mammal'. If you only caught 'Cat &c' (and not
    'Mammal'), the program will exhibit an "un-caught exception" and terminate
    which looks like a crash).

    try{
    throw fluffy;
    }
    // catch( Cat &m ){
    // cout<<"Caught a "<<m.MyType()<<std::endl;
    // }
    catch( Mammal &m ){
    cout<<"Caught a "<<m.MyType()<<std::endl;
    Cat *cat = dynamic_cast<Cat*>( &m );
    if( cat ){ cout<<"It's a Cat!!"<<std::endl;}
    }
    // out: Caught a Cat
    // out: It's a Cat!!

    throw fluffyRef;
    // out: Caught a Mammal
    // The cast fails, which I think is slicing the Cat (at throw).
     
    BobR, Jul 16, 2007
    #7
  8. stevewilliams2004

    Guest Guest

    You can not expect polymorphism works in the try/catch idiom.
    As the Holy Standard illustrates:

    A throw-expression initializes a temporary object, called the
    exception object, the type of which is determined by removing any top-
    level cv-qualifiers from the static type of the operand of throw.[1]

    For the solution of this problem, you can go to http://www.ddj.com/dept/cpp/184401940.



    1.[1] ISO/IEC 14882:2003(E), "Programming Languages-C++," 15.1 63 and
    "How do I throw polymorphically?" C++ FAQ Lite,
    http://www.parashift.com/c++-faq-lite/exceptions.html#faq-17.10.
     
    Guest, Jul 16, 2007
    #8
    1. Advertisements

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.