locked
exceptions (throw and catch) RRS feed

  • Question

  • this code :
    #include <iostream> 
    #include <string>
    using namespace std;
    class Class {
    public:
    	string msg;
    	Class(string txt):msg(txt){cout<<"Object [" << msg << "] constructed" << endl; }
    	~Class(void) { cout << "Object [" << msg << "] destructed" << endl; }
    	void Hello(void) { cout << "Object [" << msg << "] says: hello" << endl; }
    };
    void DoCalculations(int i) {
    	if(i == 0) 
    		throw Class("exception 1");
    	Class object("object");
    	if(i == 1)
    		throw Class("exception 2");
    	object.Hello();
    	if(i == 2)
    		throw Class("exception 3");
    }
    int main(void) {
        for(int i = 0; i < 3; i++) {
    	try {
    	   cout << "-------" << endl;
    	   DoCalculations(i);
    	} catch (Class &exc) {
    	   cout << "Caught!" << endl;
    	   cout << exc.msg << endl;
    	}
        }	
        return 0;
    }
    outputs this in many environments :
    -------
    Object [exception 1] constructed
    Caught!
    exception 1
    Object [exception 1] destructed
    -------
    Object [object] constructed
    Object [exception 2] constructed
    Object [object] destructed
    Caught!
    exception 2
    Object [exception 2] destructed
    -------
    Object [object] constructed
    Object [object] says: hello
    Object [exception 3] constructed
    Object [object] destructed
    Caught!
    exception 3
    Object [exception 3] destructed

    but when i run it using my visual studio express 2012 (with update 4) it displays this output :
    -------
    Object [exception 1] constructed
    Object [exception 1] destructed
    Caught!
    exception 1
    Object [exception 1] destructed
    -------
    Object [object] constructed
    Object [exception 2] constructed
    Object [exception 2] destructed
    Object [object] destructed
    Caught!
    exception 2
    Object [exception 2] destructed
    -------
    Object [object] constructed
    Object [object] says: hello
    Object [exception 3] constructed
    Object [exception 3] destructed
    Object [object] destructed
    Caught!
    exception 3
    Object [exception 3] destructed

    it displays one more "Object [exception n] destructed" statement after each "Object [exception n] constructed" statement, why does it act like this ?
    Monday, July 27, 2015 5:20 PM

Answers

  • In principle, the throw() statement creates a temporary exception object, and then copies it to some private location (it does not pass it directly to the catch).

    It would seem that your "many environments" are able to elide this copy and construct the exception object directly in the private location, but VS2012 does not do this.

    Have you tried this in VS2015?


    David Wilkinson | Visual C++ MVP

    • Marked as answer by Shu 2017 Tuesday, August 4, 2015 9:16 AM
    Monday, July 27, 2015 9:16 PM
  • i have 2 questions :

    1st: why is the const part important in this context

    2nd: i am taking a self study course in c++ which uses a different environment, and the issue of key differences between environments way of running applications is somehow annoying me, for example this code :

    include <iostream>
    using namespace std;
    class Class {
    public:
    	string msg;
    	Class(string txt) : msg(txt) {}
    };
    void function(void) throw (Class) {
    	throw string("object");
    }
    int main(void) {
        try {
    	function();
        } catch(string &exc) {
    	cout << "Caught!" << endl;
        }
        return 0;
    }
    the course says that this causes error during execution because we specified the function named function to throw entities of type Class and we instead threw string, but visual studio 2012 and 2015 run this code normally and display the "Caught!" statement located in catch.

    Visual studio ignores throw(class) specifications and will, with W4 warning level indicate that it is ignored. That's why it works the way it does. Since this kind of throw specification is now deprecated the course material you are using is somewhat dated.

    • Marked as answer by Shu 2017 Tuesday, August 4, 2015 9:17 AM
    Tuesday, July 28, 2015 7:58 PM
  • The & in the 1st constructor (between string and s) was missing and i amended the throw to be : throw A(string("yes"));

    now the throw implements the original object created directly to the catch, and the output becomes :

    original object constructed

    catch

    object destructed

    I'm really not sure why these changes made a difference (the key one was the change to passing the string by reference to the A constructor).

    However, in VS2012 at Level 4 your final code gives a bunch of warnings. Better would be the "const-aware" version:

    #include <iostream> 
    #include <string>
    using namespace std;
    class A {
    public:
      A(const string&) {cout << "original object constructed\n";}
      A(const A&) {cout << "copy object constructed\n";}
      ~A() {cout << "object destructed\n";}
    };
    
    void yes() {
    throw A("yes");
    }
    
    int main(void) {
      try {yes();} catch(const A&) {cout << "catch\n";}
      return 0;
    }
    

    This code also avoids the unnecessary copy.

    Note that the throw() cannot pass its argument directly to the catch(), because it does not know where the catch will take place. The exception object must be created in some "private area". The issue is whether this creation requires a copy.


    David Wilkinson | Visual C++ MVP

    • Marked as answer by Shu 2017 Tuesday, August 4, 2015 9:17 AM
    Tuesday, July 28, 2015 11:51 AM

All replies

  • Probably the compiler invokes certain other default constructors. Try providing more constructors:

    Class( const Class & a ) : msg( a.msg )    { cout << "Object [" << msg << "] constructed (copied)" << endl; }
    Class( Class && a ) : msg( move( a.msg ) ) { cout << "Object [" << msg << "] constructed (moved)"  << endl; }

    Then repeat the experiment.

    Monday, July 27, 2015 5:56 PM
  • Probably the compiler invokes certain other default constructors. Try providing more constructors:

    Class( const Class & a ) : msg( a.msg )    { cout << "Object [" << msg << "] constructed (copied)" << endl; }
    Class( Class && a ) : msg( move( a.msg ) ) { cout << "Object [" << msg << "] constructed (moved)"  << endl; }

    Then repeat the experiment.

    i added a copying constructor and it used it, the throw makes an object, sends a copy of it to the catch (although the '&' is written) then the throw deletes the original object and the catch just uses the copy, i don't know why
    Monday, July 27, 2015 7:02 PM
  • In principle, the throw() statement creates a temporary exception object, and then copies it to some private location (it does not pass it directly to the catch).

    It would seem that your "many environments" are able to elide this copy and construct the exception object directly in the private location, but VS2012 does not do this.

    Have you tried this in VS2015?


    David Wilkinson | Visual C++ MVP

    • Marked as answer by Shu 2017 Tuesday, August 4, 2015 9:16 AM
    Monday, July 27, 2015 9:16 PM
  • In principle, the throw() statement creates a temporary exception object, and then copies it to some private location (it does not pass it directly to the catch).

    It would seem that your "many environments" are able to elide this copy and construct the exception object directly in the private location, but VS2012 does not do this.

    Have you tried this in VS2015?


    David Wilkinson | Visual C++ MVP

    i made 2 codes which may somehow describe the strange behavior better, 1st code :

    #include <iostream> 
    #include <string>
    using namespace std;
    class A {
    public:
    	A(string s) {cout << "original object constructed\n";}
    	A(A&) {cout << "copy object constructed\n";}
    	~A() {cout << "object destructed\n";}
    };
    void yes() {
    throw A("yes");
    }
    int main(void) {
    	try {yes();} catch(A&a) {cout << "catch\n";}
        return 0;
    }

    this outputs :

    original object constructed

    copy object constructed

    object destructed

    catch

    object destructed

    2nd code :

    #include <iostream> 
    #include <string>
    using namespace std;
    class A {
    public:
    	A() {cout << "original object constructed\n";}
    	A(A&) {cout << "copy object constructed\n";}
    	~A() {cout << "object destructed\n";}
    };
    void yes() {
    throw A();
    }
    int main(void) {
    	try {yes();} catch(A&a) {cout << "catch\n";}
        return 0;
    }

    this outputs:

    original object constructed

    catch

    object destructed

    the difference between 2 codes is in the 1st constructor, now what does the problem seem ?

    Monday, July 27, 2015 9:55 PM
  • In principle, the throw() statement creates a temporary exception object, and then copies it to some private location (it does not pass it directly to the catch).

    It would seem that your "many environments" are able to elide this copy and construct the exception object directly in the private location, but VS2012 does not do this.

    Have you tried this in VS2015?


    David Wilkinson | Visual C++ MVP


    This is what is happening. The "extra" destructor call isn't really extra, it just matches a hidden call to an automatically created and called Class(const Class&). In the case of just two destructor calls the copy constructor is elided. Compilers vary in what is allowed but both of these behavior examples are legitimate and accomplish the same thing.
    Monday, July 27, 2015 11:02 PM
  • In principle, the throw() statement creates a temporary exception object, and then copies it to some private location (it does not pass it directly to the catch).

    It would seem that your "many environments" are able to elide this copy and construct the exception object directly in the private location, but VS2012 does not do this.

    Have you tried this in VS2015?


    David Wilkinson | Visual C++ MVP

    i made 2 codes which may somehow describe the strange behavior better, 1st code :

    #include <iostream> 
    #include <string>
    using namespace std;
    class A {
    public:
    	A(string s) {cout << "original object constructed\n";}
    	A(A&) {cout << "copy object constructed\n";}
    	~A() {cout << "object destructed\n";}
    };
    void yes() {
    throw A("yes");
    }
    int main(void) {
    	try {yes();} catch(A&a) {cout << "catch\n";}
        return 0;
    }

    this outputs :

    original object constructed

    copy object constructed

    object destructed

    catch

    object destructed

    2nd code :

    #include <iostream> 
    #include <string>
    using namespace std;
    class A {
    public:
    	A() {cout << "original object constructed\n";}
    	A(A&) {cout << "copy object constructed\n";}
    	~A() {cout << "object destructed\n";}
    };
    void yes() {
    throw A();
    }
    int main(void) {
    	try {yes();} catch(A&a) {cout << "catch\n";}
        return 0;
    }

    this outputs:

    original object constructed

    catch

    object destructed

    the difference between 2 codes is in the 1st constructor, now what does the problem seem ?

    In the second case the copy constructor is elided. Both examples are correct.

    Monday, July 27, 2015 11:07 PM
  • #include <iostream> 
    #include <string>
    using namespace std;
    class A {
    public:
    	A(string s) {cout << "original object constructed\n";}
    	A(A&) {cout << "copy object constructed\n";}
    	~A() {cout << "object destructed\n";}
    };
    void yes() {
    throw A("yes");
    }
    int main(void) {
    	try {yes();} catch(A&a) {cout << "catch\n";}
        return 0;
    }

    i figured out what was wrong, the new code :

    #include <iostream> 
    #include <string>
    using namespace std;
    class A {
    public:
    	A(string &s) {cout << "original object constructed\n";}
    	A(A&) {cout << "copy object constructed\n";}
    	~A() {cout << "object destructed\n";}
    };
    void yes() {
    throw A(string("yes"));
    }
    int main(void) {
    	try {yes();} catch(A&a) {cout << "catch\n";}
        return 0;
    }

    the & in the 1st constructor (between string and s) was missing and i amended the throw to be : throw A(string("yes"));

    now the throw implements the original object created directly to the catch, and the output becomes :

    original object constructed

    catch

    object destructed

    Tuesday, July 28, 2015 2:31 AM
  • The & in the 1st constructor (between string and s) was missing and i amended the throw to be : throw A(string("yes"));

    now the throw implements the original object created directly to the catch, and the output becomes :

    original object constructed

    catch

    object destructed

    I'm really not sure why these changes made a difference (the key one was the change to passing the string by reference to the A constructor).

    However, in VS2012 at Level 4 your final code gives a bunch of warnings. Better would be the "const-aware" version:

    #include <iostream> 
    #include <string>
    using namespace std;
    class A {
    public:
      A(const string&) {cout << "original object constructed\n";}
      A(const A&) {cout << "copy object constructed\n";}
      ~A() {cout << "object destructed\n";}
    };
    
    void yes() {
    throw A("yes");
    }
    
    int main(void) {
      try {yes();} catch(const A&) {cout << "catch\n";}
      return 0;
    }
    

    This code also avoids the unnecessary copy.

    Note that the throw() cannot pass its argument directly to the catch(), because it does not know where the catch will take place. The exception object must be created in some "private area". The issue is whether this creation requires a copy.


    David Wilkinson | Visual C++ MVP

    • Marked as answer by Shu 2017 Tuesday, August 4, 2015 9:17 AM
    Tuesday, July 28, 2015 11:51 AM
  • i have 2 questions :

    1st: why is the const part important in this context

    2nd: i am taking a self study course in c++ which uses a different environment, and the issue of key differences between environments way of running applications is somehow annoying me, for example this code :

    #include <iostream>
    using namespace std;
    class Class {
    public:
    	string msg;
    	Class(string txt) : msg(txt) {}
    };
    void function(void) throw (Class) {
    	throw string("object");
    }
    int main(void) {
        try {
    	function();
        } catch(string &exc) {
    	cout << "Caught!" << endl;
        }
        return 0;
    }
    the course says that this causes error during execution because we specified the function named function to throw entities of type Class and we instead threw string, but visual studio 2012 and 2015 run this code normally and display the "Caught!" statement located in catch.


    Tuesday, July 28, 2015 2:36 PM
  • i have 2 questions :

    1st: why is the const part important in this context

    2nd: i am taking a self study course in c++ which uses a different environment, and the issue of key differences between environments way of running applications is somehow annoying me, for example this code :

    include <iostream>
    using namespace std;
    class Class {
    public:
    	string msg;
    	Class(string txt) : msg(txt) {}
    };
    void function(void) throw (Class) {
    	throw string("object");
    }
    int main(void) {
        try {
    	function();
        } catch(string &exc) {
    	cout << "Caught!" << endl;
        }
        return 0;
    }
    the course says that this causes error during execution because we specified the function named function to throw entities of type Class and we instead threw string, but visual studio 2012 and 2015 run this code normally and display the "Caught!" statement located in catch.

    In your previous code, you should set your warning level to 4 and also treat warnings as errors (always do this!) and see what errors you get. The main one is binding a temporary to a non-const reference. If you disable language extensions then this will show up as an error rather than a warning.

    If you disable language extensions I think you will generally see better agreement between Visual C++ and other environments.

    As to exception specifications in Visual C++, see

    https://msdn.microsoft.com/en-us/library/wfa0edys.aspx?f=255&MSPPError=-2147217396

    Note that exception specifications are deprecated in C++11.


    David Wilkinson | Visual C++ MVP

    Tuesday, July 28, 2015 3:25 PM
  • i have 2 questions :

    1st: why is the const part important in this context

    2nd: i am taking a self study course in c++ which uses a different environment, and the issue of key differences between environments way of running applications is somehow annoying me, for example this code :

    include <iostream>
    using namespace std;
    class Class {
    public:
    	string msg;
    	Class(string txt) : msg(txt) {}
    };
    void function(void) throw (Class) {
    	throw string("object");
    }
    int main(void) {
        try {
    	function();
        } catch(string &exc) {
    	cout << "Caught!" << endl;
        }
        return 0;
    }
    the course says that this causes error during execution because we specified the function named function to throw entities of type Class and we instead threw string, but visual studio 2012 and 2015 run this code normally and display the "Caught!" statement located in catch.

    Visual studio ignores throw(class) specifications and will, with W4 warning level indicate that it is ignored. That's why it works the way it does. Since this kind of throw specification is now deprecated the course material you are using is somewhat dated.

    • Marked as answer by Shu 2017 Tuesday, August 4, 2015 9:17 AM
    Tuesday, July 28, 2015 7:58 PM
  • Note that exception specifications are deprecated in C++11.

    does that mean that the set_unexpected() function is of no use now with the unspecified exceptions ?
    Thursday, July 30, 2015 3:27 PM
  • Note that exception specifications are deprecated in C++11.

    does that mean that the set_unexpected() function is of no use now with the unspecified exceptions ?

    That's right. It is also deprecated. The entire idea of exception specifications has been found to be not useful. Exception handling best practices are to limit function exception characteristics to whether or not it throws via the "noexcept" qualifier.


    EtoA: noexcept was implemented in VC in VS2015 as is required for C++11 compliance.
    • Edited by vcangel Thursday, July 30, 2015 5:34 PM
    Thursday, July 30, 2015 5:20 PM
  • mmm thank you, the course i am taking is working on netbeans IDE, maybe it is using older verion like C++03.
    Thursday, July 30, 2015 11:06 PM
  • i have to mention something, i installed visual studio 2015 and it behaved normally with the first code in the first post of this topic, it didn't copy the object, so it was probably kind of weakness in VS 2012
    Saturday, August 1, 2015 11:32 AM
  • i have to mention something, i installed visual studio 2015 and it behaved normally with the first code in the first post of this topic, it didn't copy the object, so it was probably kind of weakness in VS 2012

    That's right. It's a redundancy and an inefficiency, but not an error in VS2012.

    The default copy constructors are elided in VS2015 but not in VS2012. Generally, I've found VS2015 does a better job of not creating unnecessary, and sometimes hidden, constructors.

    One area of confusion that sometimes comes up in many areas are copy constructors with side effects. C++ allows compilers to bypass copy constructors where possible and any side effects such as logging are skipped. This can be confusing. A good programming practice is never to put anything in a copy constructor beyond what is necessary to make a copy as there is a good chance it will never be invoked if redundant constructors can be optimized away.

    Saturday, August 1, 2015 2:31 PM