I have 3 questions :
1. It is always said to catch the exception object by reference. In the following example i see that the destructor is called before the catch block executed, that means we are referring to an object in catch which must have gone out of scope by the time we use it. Why use reference then?
class MyException{
public:
~MyException() {
cout<<"Dtor for MyException called \n";
}
};
int main()
{
try{
MyException obj1;
throw obj1;
}
catch(MyException& obj)
{
cout<<"Catched unhandled exception";
}
}
2. Why is the destructor called twice here? Once before entering the catch block and second time after execution of catch completes.
3. The example in Lifetime of a thrown object caught by reference shows that the destructor is called just once i.e. after the catch block exits ONLY WHEN a copy constructor is defined in the class.
a. What is the role of copy constructor here when we are catching it by refrence?
b. Even after defining a copy constructor it never gets called anyways. So what impact does it make?
c. I tried to define a copy constructor in my example too as below but still i see destructor getting called twice.Why?:
MyException(const MyException& obj)
{
}
Can somebody answer all the 5 questions(3rd question has 3 parts).
You're throwing a copy of the exception you created as a local temporary. So there's two: the first is destroyed when you throw, the second (the copy) after the catch block is done.
Try this:
try { throw MyException{}; }
catch (MyException const& obj) { }
Edit: If your copy constructor is really not being called in your code then my guess is that the compiler has recognized that the exception class is empty? Compiler is free to perform any optimizations it chooses so long as the behavior of the code is as-if it hadn't. Exception to this is copy-ellision but if that was going on you'd not get the double destructor call.
throw ALWAYS makes a copy of an object you're throwing. You can't throw an object which is not copyable. If fact, throw can even fail if it can't allocate enough memory for a copy. This should explain why you see a destructor being called inside a try-block. It's a destructor of your local obj1. It goes out of scope as soon as your throw a copy of it.As for catching by reference, it is used to avoid unnecessary copying and, more importantly, potential slicing.
Once again, it was a destructor of your local obj1. It went after out scope when you threw a copy of it.
a) If you catch by reference, no copying is performed. Copying (with potential slicing) is performed ONLY if you catch by value.
b) You're wrong, throw calls a copy constructor to, well, make a copy of your object and throw it. The only case when a copy constructor won't be called is when you throw a temporary object and compiler elides a copy.
c) Refer to my first and second answer.
They recommend you take your catch block by reference to avoid slicing (since inheriting exceptions is standard) and to avoid needlessly copying, since throw copies unconditionally.
You cannot receive obj1 by reference as throw obj1 causes it to be destroyed (since it is no longer in scope). Due to this throw always copies to temporary memory location that you don't control.
obj1 and the temporary I mentioned before. obj is a reference to that temporary, the referenced temporary will be destroyed after the end of the catch block and before the next operation (similar to if it were actually local to the catch block).
A single destructor only happens when the copy is elided and the standard doesn't guarantee when that will happen, as copy elision is always optional. Defining a copy constructor makes no difference as the compiler defines it for you if you don't define it (and it can be created). Deleting the copy constructor would just result in throw being illegal.
a. As above, copy constructor to copy into the temporary. Reference to reference said temporary.
b. Did you actually delete the copy constructor? MyException(const MyException&) = delete; is how you delete a copy constructor, failing to define it just causes the compiler to make one for you. And to repeat, deleting it causes throw to be illegal.
c. Because the copy isn't elided and two objects exist, a temporary and your object.
Related
Why throwing exception which is a reference calls copy constructor?
struct Error
{
Error() {}
Error(const Error&) = delete;
};
int main()
{
Error& error = *new Error;
throw error;
}
Compilation error:
error: declared here
Error(const Error&) = delete;
It does not happen when throwing pointer like:
int main()
{
Error* error = new Error;
throw error;
}
This is OK.
You cannot throw a reference. Throwing always copies the thrown expression value into a special area of storage set aside for thrown objects. Otherwise, you'd almost always be "catching" a dangling reference, as is [theoretically] the case in your code.
Your Error type cannot be copied, so the program is impossible.
However, a pointer can of course be copied, and the main problem in your final example is a memory leak. Also your program will simply terminate at the throw statement as you don't have any try/catch.
Before unwinding stack a throw operator (except throw; without an argument, used for rethrowing) creates an exception object (in a special memory area). Depending on circumstances, the object is initialized in different ways: constructor, copy constructor, move constructor (https://en.cppreference.com/w/cpp/language/copy_elision) using what was provided to the throw operator. Providing the reference is ok, but three:
if you provide a reference in an arguments list, it depends on receiving party, what is actually received, a reference or a value copy;
compiler needs to initialize an exception object, because what was provide to the throw operator will not live when the exception handling catch block is running (the stack will have been unwound then; in case of the pointer, the pointer provided, though the object it points to in your case is alive, and in the catch block you have a copy of the pointer to the same object);
it is not possible to initialize a reference in runtime;
so, in your case, the compiler expects the copy constructor in order to initialize an exception object using the reference you provided (copy constructors usually takes values to initialize the object using references to the initial one).
When you pass a reference to Error to the throw operator, the type of the exception object is Error, and we need to initialize an Error exception object in that specific memory area..
When you pass a pointer to Error to a throw operator, the type of the exception object is a pointer to Error (Error *), so the pointer is copied, not the Error object which the pointer points to. The copying pointer to error has nothing to do with calling the copy constructor of Error, so you don’t have the error in that case.
The following code was compiled and run in Visual Studio 2012 Express for Windows Desktop, as a learning exercise.
#include <cstdio>
class X
{
public:
X() { printf("default constructed\n"); }
~X() { printf("destructed\n");}
X(const X&) { printf("copy constructed\n"); }
X(X&&) { printf("move constructed\n"); }
X & operator= (const X &) { printf("copy assignment operator\n"); }
};
X A() {
X x;
return x;
}
int main() {
{
A();
}
std::getchar();
}
When compiled with compiler optimizations disabled (/Od), the resulting output indicates that the destructor is called twice. This is a problem given that only one object is constructed. Why is the destructor being called twice? Wouldn't this be a problem if the class was managing it own resources?
default constructed
move constructed
destructed
destructed <<< Unexpected call
I tried a couple of experiments to try and explain the output, but ultimately these didn't lead to any useful explanations.
Experiment 1: When the same code is compiled with optimizations enabled (/O1 or /O2), the resulting output is:
default constructed
destructed
which indicates that the Named Return Value Optimization has elided the call to the move constructor, and masked the underlying problem.
Experiment 2: Disabled the optimization and commented out the move constructor. The output generated was what I expected.
default constructed
copy constructed
destructed
destructed
Keep in mind that when an object is the source of a move operation it will still be destroyed. So the source of the move needs to put itself in a state such that being destructed will not release resources that it no longer owns (since they were moved to another object). For example, any raw pointers (that will now be owned by the move constructed object) in the source object should be set to NULL.
The X in A is destroyed when it goes out of scope.
A returns a temporary object (constructed from X by the move constructor) which is a separate instance. This is destroyed in the caller's scope. That will cause the destructor to be called again (on the temporary).
The move constructor was picked because the compiler detected that X was going to be destroyed immediately afterward. To use this approach, the move constructor should nullify or reset any data in the original object so that the destructor will not invalidate any data that has been taken over by the destination of the move.
When you pass an rvalue by value, or return anything by value from a function, the compiler first gets the option to elide the copy. If the copy isn’t elided, but the type in question has a move constructor, the compiler is required to use the move constructor.
http://cpp-next.com/archive/2009/09/move-it-with-rvalue-references/
When you exit from the scope in which the temporary object was created, it is destroyed. If a reference is bound to a temporary object, the temporary object is destroyed when the reference passes out of scope unless it is destroyed earlier by a break in the flow of control.
http://publib.boulder.ibm.com/infocenter/comphelp/v8v101/index.jsp?topic=%2Fcom.ibm.xlcpp8a.doc%2Flanguage%2Fref%2Fcplr382.htm
The RVO can produce different behavior from the non-optimized version:
Return value optimization, or simply RVO, is a compiler optimization technique that involves eliminating the temporary object created to hold a function's return value.[1] In C++, it is particularly notable for being allowed to change the observable behaviour of the resulting program.[2]
http://en.wikipedia.org/wiki/Return_value_optimization
Although Michael's and jspcal answers are accurate, they didn't answer the heart of my question, which was why were there two destructor calls made. I was expecting just one.
The answer is that function A() returns a temporary object. Always. This is how function return values work, and move-semantics has no bearing on this fact. I guess Michael and jspcal assumed that I had not missed such a basic fact. I equated the term "moved" with the concept of "swap". When swapped, objects are not constructed and destructed. Hence I was expecting just one destructor call.
Since the returned object must be constructed and destructed, the second destructor call was made (and a second constructor call).
Now, the actual constructor selected to be executed depends on what is provided in the class definition. If a move-constructor is available, that will be called. Otherwise the copy constructor will be called.
http://www.drdobbs.com/cpp/practical-c-error-handling-in-hybrid-env/197003350?pgno=4
In this article Herb Sutter explains that throwing an exception requires a copy of the exception as it's created as a temporary and therefore uses an std::auto_ptr to get round the copy overhead. In light of move semantics being made available in C++11 is this still necessary?
I have just checked, and the Standard allows
omitting the copy or move of an object specified by the operand of a throw expression into the exception object
omitting the copy or move of the exception object into the catch clause variable of the same type as the exception object if you don't otherwise change the meaning of the program (i.e if you would rethrow and subsequent catches would suddenly see a changed exception object changed by the previous catch block).
Since these omissions are allowed, the spec requires to first regard the source of the copy or move as an rvalue. So this means that the respective objects will be moved if possible. Of course copy and move elision are still allowed as the first choice.
Update
I was notified that the consideration of the exception object initializer of a catch clause parameter as an rvalue initializer will probably be dropped from the Standard (because in general it is not possible for all cases to detect when the behavior of the program is unchanged when omitting a copy/move), so I recommend to not rely on this (second bullet above).
What you can still rely about is the move of a local variable into the exception object, as in throw x; (first bullet above).
Move from exception objects is not mandatory now.
It's a defect of C++11. See CWG1493 .
Upon throw expression, a copy of the exception object always needs to be created as the original object goes out of the scope during the stack unwinding process.During that initialization, we may expect copy elision (see this) – omits copy or move constructors (object constructed directly into the storage of the target object). But even though copy elision may or may not be applied you should provide proper copy constructor and/or move constructor which is what C++ standard mandates(see 15.1). See below compilation error for reference.
But modern C++ provides more feature: "Moving safely with move semantics & exception"
struct demo
{
demo() = default;
demo(const demo &) { cout << "Copying\n"; }
// Exception safe move constructor
demo(demo &&) noexcept { cout << "Moving\n"; }
private:
std::vector<int> m_v;
};
int main()
{
demo obj1;
if (noexcept(demo(std::declval<demo>()))){ // if moving safe
demo obj2(std::move(obj1)); // then move it
}
else{
demo obj2(obj1); // otherwise copy it
}
demo obj3(std::move_if_noexcept(obj1)); // Alternatively you can do this----------------
return 0;
}
We can use noexcept(T(std::declval<T>())) to check if T’s move constructor exists and is noexcept in order to decide if we want to create an instance of T by moving another instance of T (using std::move). Alternatively, we can use std::move_if_noexcept, which uses noexcept operator and casts to either rvalue or lvalue. Such checks are used in std::vector and other containers.This will be useful while you are processing critical data which you don't want to lose. For example, we have critical data received from the server that we do not want to lose it at any cost while processing. In such a case, we should use std::move_if_noexcept which will move ownership of critical data only and only if move constructor is exception-safe.
From: 7 best practices for exception handling in C++
class Error1
{
public:
int errorcode;
Error1(int x):errorcode(x){ cout<<"CTOR Error1"<<endl; }
//Error1(Error1& obj ){
// errorcode = obj.errorcode;
// cout<<"CopyCTOR Error1"<<endl;
//}
~Error1(){cout<<"DTOR Error1"<<endl; }
};
void fun()
{
cout<<"Inside fun"<<endl;
throw(Error1(5));
}
int main()
{
try{
fun();
}
catch(Error1& eobj)
{
cout<<"Error1 type occured with code:"<<eobj.errorcode<<endl;
}
cin.get();
}
OUTPUT:
Inside fun
CTOR Error1
DTOR Error1
Error1 type occured with code:5
DTOR Error1
This output indicates that a Error1 object is copy constructed for the catch handler. Since copy constructor is not defined for Error1 object default copy constructor is used.
When i uncomment the commented section for defining a copy constructor i get the the following output.
Inside fun
CTOR Error1
Error1 type occured with code:5
DTOR Error1
Why is it that only one DTOR is getting called? Even if exception is caught by reference i believe a temporary is still created.
What compiler are you using?
When you introduce (i.e. uncomment) your version of copy constructor with Error1& obj argument, the code is supposed to become invalid. throw is supposed to be able to create a copy of its argument, while your version of copy constructor disables copying of temporaries. The code is ill-formed. If your compiler accepts it, its is probably because it illegally allows binding non-const references to temporaries (I suspect it is MSVC++ compiler with extensions enabled).
The original experiment works as it is supposed/allowed to. The argument of throw is copied to an internal temporary which is later used to initialize catch parameters. Although the compilers are allowed to use your original temporary directly, extending its lifetime accordingly.
There may be other errors, but what I see right now is that throw(Error1(5)); creates a temporary (or rvalue) of type Error1. You want an lvalue, which means that you should either do throw(*new Error1(5)); (which I believe will create a memory leak, but I may be wrong), or you could create a global Error1 object and just throw that.
PS: I would be interested to know if throw(*new Error1(5)); does create a memory leak, if anyone would like to comment. Does catch destroy the object it catches? If so I think you should be fine with just creating new Error1s whenever you need them.
I've come across some exceptions issue that is unclear to me. In C++, when an object is thrown it is first copied to a temporary object, and the temporary object is then passed to the catching code. The copy involves the use of the object's class copy constructor. AFAIK, this means that if a class has a private copy constructor, it can't be used as an exception. However, in VS2010, the following code compiles and runs:
class Except
{
Except(const Except& other) { i = 2; }
public:
int i;
Except() : i(1) {}
};
int main()
{
try
{
Except ex1;
throw ex1; // private copy constructor is invoked
}
catch (Except& ex2)
{
assert(ex2.i == 2); // assert doesn't yell - ex2.i is indeed 2
}
return 0;
}
Is this legal?
It's not legal. Standard 15.1/5
If the use of the temporary object can be eliminated without changing
the meaning of the program except for the execution of constructors
and destructors associated with the use of the temporary object
(12.2), then the exception in the handler can be initialized directly
with the argument of the throw expression. When the thrown object is a
class object, and the copy constructor used to initialize the
temporary copy is not accessible, the program is ill-formed (even when
the temporary object could otherwise be eliminated). Similarly, if the
destructor for that object is not accessible, the program is
ill-formed (even when the temporary object could otherwise be
eliminated).
No, it's not.
15.1.5 When the thrown object is a class object, the copy/move constructor and the destructor shall be accessible,
even if the copy/move operation is elided