try object out of scope before catch?

  • Thread starter Jackson A. Marshall
  • Start date
J

Jackson A. Marshall

I have a been testing the try/catch exception mechanism
(as implemented by DigitalMars) and found that if I
throw an object its dtor gets called 3 times, if I throw
a reference its dtor gets called twice - and so it seems
practical to instantiate an object and throw its address,
and sure enough, its dtor gets called only once.

This is part of the Test class implementation (contains only
one data member: "int n" ):

volatile int count = 0;

// Constructor
Test::Test() : n(0) {
++::count;
std::cout << " Test(" << ::count << ")\n";
}

// Constructor
Test::Test(int nn) : n(nn) {
++::count;
std::cout << " Test(" << ::count << ")\n";
}

// Destructor
Test::~Test() {
std::cout << "~Test(" << ::count << ")\n";
--::count;
}

// e.g. Test1 += Test2;
const Test& Test::eek:perator+=(const Test& rhs) {
std::cout << "operator+=(Test&)\n";
if(this == &rhs) {
Except ex("operator+= self assignment!");
throw( &ex );
}
n += rhs.n;
return *this;
}

The Except class implementation..

volatile int except = 0;

// Constructor
Except::Except(const char* msg = 0) : data(msg) {
++::except;
std::cout << " Except(" << ::except << ")\n";
}

// Destructor
Except::~Except() {
std::cout << "~Except(" << ::except << ")\n";
--::except;
}

// access data
const char* Except::Get() { return data; }


int main() {
Test t;
try { t += t; }
catch( Except* e ) {
cout << "Throw() caught: " << e->Get() << "\n";
return -1;
}
}

Output:
======
Test(1)
operator+=(Test&)
Except(1)
~Except(1)
Throw() caught: operator+= self assignment!
~Test(1)

But this implies that Except goes out of scope before
the throw is caught. I would have liked to have seen:

Test(1)
operator+=(Test&)
Except(1)
Throw() caught: operator+= self assignment!
~Except(1)
~Test(1)

Anyone comments?

TIA
 
V

Victor Bazarov

Jackson said:
I have a been testing the try/catch exception mechanism
(as implemented by DigitalMars) and found that if I
throw an object its dtor gets called 3 times, if I throw
a reference its dtor gets called twice - and so it seems
practical to instantiate an object and throw its address,
and sure enough, its dtor gets called only once.

This is part of the Test class implementation (contains only
one data member: "int n" ):
[...]

Anyone comments?

Here is a comment: regarding your code and the sequence of d-tors,
you're throwing an address of a local variable. It gets destroyed
before you catch it. When you after catching the value of the
pointer attempt to use it, you get undefined behavour.

And also your 'Test' class doesn't seem to have any copy c-tor
implemented, thus making it impossible for you to track copy
construction (not that you should expect any, but still).

If you want to throw by an address (pointer), you have to use
either a global object (that survives the stack unwinding) or
a dynamic object (use 'new'). Of course, it's better to throw
a temporary object and catch by a const reference.

V
 
J

Jackson A. Marshall

Victor Bazarov said:
Jackson said:
I have a been testing the try/catch exception mechanism
(as implemented by DigitalMars) and found that if I
throw an object its dtor gets called 3 times, if I throw
a reference its dtor gets called twice - and so it seems
practical to instantiate an object and throw its address,
and sure enough, its dtor gets called only once.

This is part of the Test class implementation (contains only
one data member: "int n" ):
[...]

Anyone comments?

Here is a comment: regarding your code and the sequence of d-tors,
you're throwing an address of a local variable. It gets destroyed
before you catch it. When you after catching the value of the
pointer attempt to use it, you get undefined behavour.

And also your 'Test' class doesn't seem to have any copy c-tor
implemented, thus making it impossible for you to track copy
construction (not that you should expect any, but still).

If you want to throw by an address (pointer), you have to use
either a global object (that survives the stack unwinding) or
a dynamic object (use 'new'). Of course, it's better to throw
a temporary object and catch by a const reference.

Thanks V

I do have a copy-ctor, just didn't show it:

// Copy Constructor
Test::Test(Test& t) : n(t.n) {
++::count;
std::cout << " Test(" << ::count << ") Copy-Ctor\n";
}

Using "new" :

// e.g. Test1 += Test2;
const Test& Test::eek:perator+=(const Test& rhs) {
std::cout << "operator+=(Test&)\n";
if(this == &rhs) {
Except* ex = new Except("operator+= self assignment!");
throw( ex );
}
n += rhs.n;
return *this;
}

// copy ctor: pass by value
void test1(Test a) {}

int main() {
Test t;
test1(t); // copy ctor
cout << "\n";

try { t += t; }
catch( Except* e) {
cout << "Throw() caught: " << e->Get() << "\n";
delete e;
return -1;
}
}

Output:
=====
Test(1)
Test(2) Copy-Ctor
~Test(2)

operator+=(Test&)
Except(1)
Throw() caught: operator+= self assignment!
~Except(1)
~Test(1)
===============

What do you mean by (and how do I construct) a "temporary object"?
 
V

Victor Bazarov

Jackson said:
[...]
What do you mean by (and how do I construct) a "temporary object"?

Try

const Test& Test::eek:perator+=(const Test& rhs) {
std::cout << "operator+=(Test&)\n";
if (this == &rhs)
throw(Except("operator+= self assignment!")); // temporary

n += rhs.n;
return *this;
}
...
int main() {
Test t;
try { t += t; }
catch(Except const &e) {
cout << "Caught " << e.Get() << endl;
}
return -1;
}

V
 
J

Jackson A. Marshall

Victor Bazarov said:
Jackson said:
[...]
What do you mean by (and how do I construct) a "temporary object"?

Try

const Test& Test::eek:perator+=(const Test& rhs) {
std::cout << "operator+=(Test&)\n";
if (this == &rhs)
throw(Except("operator+= self assignment!")); // temporary

n += rhs.n;
return *this;
}
...
int main() {
Test t;
try { t += t; }
catch(Except const &e) {
cout << "Caught " << e.Get() << endl;
}
return -1;
}

Thanks V

I have implemented the code changes as you suggest.

Test(1)
operator+=(Test&)
Except(1)
~Except(1)
Throw() caught: operator+= self assignment!
~Except(0)
~Test(1)

...but this is incorrect with 2 Except dtors!
 
V

Victor Bazarov

Jackson said:
Victor Bazarov said:
Jackson said:
[...]
What do you mean by (and how do I construct) a "temporary object"?

Try

const Test& Test::eek:perator+=(const Test& rhs) {
std::cout << "operator+=(Test&)\n";
if (this == &rhs)
throw(Except("operator+= self assignment!")); // temporary

n += rhs.n;
return *this;
}
...
int main() {
Test t;
try { t += t; }
catch(Except const &e) {
cout << "Caught " << e.Get() << endl;
}
return -1;
}


Thanks V

I have implemented the code changes as you suggest.

Test(1)
operator+=(Test&)
Except(1)
~Except(1)
Throw() caught: operator+= self assignment!
~Except(0)
~Test(1)

..but this is incorrect with 2 Except dtors!

Are you sure that you got every Except's _constructor_ accounted for?
What about the copy constructor?
 
J

Jackson A. Marshall

Victor Bazarov said:
Are you sure that you got every Except's _constructor_ accounted for?
What about the copy constructor?

Many thanks V

I didn't have an Except copy-ctor, but do now :

// Copy Constructor
Except::Except( const Except& ex) : data(ex.data) {
++::except;
std::cout << " Except(" << ::except << ") Copy-Ctor\n";
}

Output:
Test(1)
operator+=(Test&)
Except(1)
Except(2) Copy-Ctor
~Except(2)
Throw() caught: operator+= self assignment!
~Except(1)
~Test(1)

Thanks again
 

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. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,999
Messages
2,570,243
Members
46,836
Latest member
login dogas

Latest Threads

Top