howtechstuffworks
howtechstuffworks

Reputation: 1916

Exception Handling + polymorphism, doesnt work if the exception method, doesnt work in the same class

I am trying some code like this

//A.hpp
 class A{
   public:
       A() {} 
       virtual const char *message() const {return "A ERROR";}
 };

 //B.hpp

 #include "A.hpp"

 class B:public A {
   public:
       B() {}
       const char *message() const {return "B ERROR";}
 };

//main.cpp
 #include "A.hpp"
 #include "B.hpp"

void foo(const A& a) {
   /* case 1 */ throw a;   /* (or) */ /* case 2 */ throw B(); // LINE 100
}

int main() {

  B b;
  A &a(b);
  b.message(); // OUTPUT: B ERROR

  try {
      foo(a);
  } catch (const A& a) {
     std::cout<<"EXCEPTION CALLED "<<a.message()<<std::endl;
  }
  return 0;
}

in this case, if I use case 1: throw a; //a is an reference of B b; OUTPUT: A ERROR

case 2: throw B(); // creates new B; OUTPUT: B ERROR

What I don't understand is, why doesn't there is no consistency betweek two cases,

If you pass by reference, all the time, there should be some consistency, If I create a new variable inside the function, that is called in the try block, then it calls the correct virtual method, else it doesn't..... Can anyone tell me the control flow.... Please advise....

Upvotes: 2

Views: 1804

Answers (4)

Luc Danton
Luc Danton

Reputation: 35449

For the sake of completeness I feel the need to point out that just like virtual copy (also known as cloning) solves copying through a base, virtual (re)throw solves throwing through a base:

struct base {
    virtual void
    rethrow() const
    { throw *this; }

    // it's also usual to make the base type abstract
    // so that users can't slice, e.g. boost::exception
    // it's also possible to make copying protected
};

struct derived: base {
    void
    rethrow() const override
    { throw *this; }
};

void
foo(base const& b)
{
    // no: slices
    // throw b;

    b.rethrow(); // Ok
}

Upvotes: 2

Branko Dimitrijevic
Branko Dimitrijevic

Reputation: 52107

Because an object is copied before thrown.

Even if the parameter a of foo points to an instance of B at run-time, what matters is the compile-time type of the throw expression. So, effectively, the instance of B is passed to the copy-constructor of A (which is legitimate since B inherits A) and a new A instance created and then thrown.

The reason for copy is that compiler must guarantee the lifetime of the exception object as long as there is any catch block that could catch it. So, it can't risk a stack object "falling off the stack edge" or heap object being deallocated by some destructor invoked during stack unwinding.

Upvotes: 5

Tod
Tod

Reputation: 8242

I'm having a little trouble understanding exactly what you are asking but... First, don't name the variable of your catch the same name as your local variable, that is you are using "a" twice to represent different things. You may be thinking that the item you catch by reference is the a you passed into foo and that will not be true if foo() throws B. Just for clarity change your catch to

catch (const A& ex)
{
 ...ex.message()...
}

I suspect what is happening is that when declared outside the try, a is still in scope and a.message() calls the local variable a. If declared inside the try, a is no longer in scope in the catch, so the a you are catching by reference calls its message. Changing the variable name of the catch should remove the seemingly ambiguous behavior.

Upvotes: -1

Jarryd
Jarryd

Reputation: 1322

Throwing copies variables. Your throw a in foo doesn't actually throw the reference to a from main, it in fact throws a copy of a. In the catch statement, you're catching that copy by reference. Since in foo, a is a reference to an A, the copy slices the object, and it becomes an A, thus losing the fact that it ever was a B.

Upvotes: 1

Related Questions