It's not really a problem but rather a need for clarification. Coming from a C background, I understand only a little of the finer concepts of cons/destructors, especially during the dreaded throws during construction of a class.
I know you can throw exceptions from constructors (which raises some eyebrows for some). If, however, my constructor was allocating things in the heap and throws, what is the best way to free up the allocated resources? Within the scope of the constructor, or the function/method which called the throwing constructor?
Also, when does a destructor get called? I'm aware that a heaped class calls its destructor during delete calls, but I don't know when they are called if they are on the stack. Do they get called when they go out of scope, or when the program exits? Should I use my destructor to deallocate memory which was allocated by the destructor, or should I use a wrapper class that does that for me? Also, if I allocate a class on a heap, do the classes they instantiate also reside in the heap, even if they don't use new?
I've only been doing light reading about RAII, so I'm not sure if I understand the concept fully (I'm not even using C++11 yet, but I plan to learn it soon just for the sake of using <memory> fully), so someone who can also shed light to how to apply it properly would be appreciated.
Sorry if it doesn't sound much of a question, but I've been reading many different books that fail to fully enlighten me about exception safety with classes.
If, however, my constructor was allocating things in the heap and throws, what is the best way to free up the allocated resources? Within the scope of the constructor, or the function/method which called the throwing constructor?
The constructor itself. http://www.parashift.com/c++-faq-lite/ctors-can-throw.html says that if a contructor throws it won't leak memory, which leads me to believe that when you get to the catch block the object will have been deallocated already (deallocated, not destructed), so you can't use the pointer to access its members and delete them.
Also, when does a destructor get called? I'm aware that a heaped class calls its destructor during delete calls, but I don't know when they are called if they are on the stack. Do they get called when they go out of scope, or when the program exits?
When they go out of scope. When the program exits the objects go out of the scope of main() so their destructors are called (in a normal situation, I'm not sure what happens if you call exit() ). By the way when a program ends the OS frees up all the memory it had allocated for it.
Should I use my destructor to deallocate memory which was allocated by the destructor, or should I use a wrapper class that does that for me?
Destructor. It is its most important job. You can't deallocate private variables from outside the class anyway. But I'm not sure what you're thinking when you say "wrapper class".
Also, if I allocate a class on a heap, do the classes they instantiate also reside in the heap, even if they don't use new?
Yes. The instance is a part of the class, and all the class goes on the heap.
About RAII, I haven't really studied it well, sorry.
#include <memory>
#include <stdexcept>
#include <iostream>
class Object {};
class MyClass
{
std::unique_ptr<Object> _object;
void methodThatWillCauseException()
{
throw std::runtime_error("ERROR: gratuitous exception");
}
public:
MyClass() : _object(new Object)
{
methodThatWillCauseException();
//when the constructor throws, the destructor for _object will be called,
// causing the destructor for the Object to be called and the memory deallocated.
}
};
int main()
{
try {
MyClass Instance;
}
catch (std::exception& ex)
{
std::cout << ex.what() << '\n';
}
}
> According to the article though, the objects allocated by the constructor aren't destroyed,
> only the constructor itself.
If a constructor throws, every sub-object that has been constructed would be destroyed in reverse order of construction (before the exception is propagated). Just use RAII consistently, and leave the rest to the C++ implementation - it knows how to manage resources correctly, precisely.
#include <iostream>
struct A
{
A( int i ) : v(i)
{
if( v > 102 ) { std::cout << "\n*** throw ***\n\n" ; throw 100 ; }
std::cout << "A::constructor v == " << v << " -- " ;
}
~A() { std::cout << "A::destructor v == " << v << '\n' ; }
constint v ;
};
struct B
{
B( int i ) : a(i) { std::cout << "B::constructor: object at " << this << '\n' ; }
~B() { std::cout << "B::destructor: object at " << this << " -- " ; }
A a ;
};
struct C
{
C( int i ) : one(i), many { i+1, i+2, i+3, i+4 }
{ std::cout << "C::constructor\n" ; }
~C() { std::cout << "C::destructor\n" ; }
B one ;
B many[4] ;
staticvoid* operatornew( std::size_t sz )
{ std::cout << "\n+++ C::operator new\n\n" ; return ::newchar[sz] ; }
staticvoidoperatordelete( void* p )
{
std::cout << "\n+++ C::operator delete\n\n" ;
return ::deletestatic_cast<char*>(p) ;
}
// ... new[], delete[]
};
int main()
{
// no exceptions are thrown
try { C c1(0) ; std::cout << "ok\n" ; }
catch( int ) { std::cout << "constructor of c1 threw\n" ; }
// objects constructed are destroyed in reverse order
std::cout << "\n-------------------------------------\n" ;
// constructor of C : first construct members (5 object of type B)
// each constructor of B : first construct members (an object of type A)
// constructor of A
// after three objects of type B are created, C::one, C::many[0], C::many[1]
// the constructor of the fourth B C::many[2] =>
// constructor of A with i == 103 => which throws
// the objects which had been constructed are destroyed in reverse order
// of construction: C::many[1], C::many[0], C::one
// (each of which destroys the contained sub-object of type A)
try { C c2(100) ; std::cout << "ok\n" ; }
catch( int ) { std::cout << "\n*** constructor of c2 threw ***\n" ; }
std::cout << "\n-------------------------------------\n" ;
// same as above, the storage duration does not make a difference
// except that memory that was allocated by C::operator new
// would be released by C::operator delete
try { C* p = new C(100) ; std::cout << "ok\n" ; delete p ; }
catch( int ) { std::cout << "\n*** constructor of C threw ***\n" ; }
}
So, in-class initializers destroy the objects that were initialized if the constructor throws, which in turn calls the destructor of whatever the initialized objects initialized themselves, thus completing an exception safe system of initializing a series of classes, right?
Okay, thanks for clarifying that. Truth to tell, I needed to understand this concept for creating my own wrapper classes for DX9 devices which becomes a little ugly once the code becomes to cropped up with if-else and try-catch at the most unlikely places.
Just one last question, even though my original question was already answered: If an object being initialized at the in-class constructor throws, will it also call the destructor chain?
EDIT: Experimented with it, and learned that all the fully constructed objects will be destroyed if the constructed chain throws, provided that non of them were initialized by the new operator.
However, using auto_ptr and shared_ptr for members that needed new to be allocated made the process safer, since it destroys it anyway when it is no longer handled.
Well, this was one eventful day in learning C++ memory handling. :)
> provided that non of them were initialized by the new operator.
You are missing the point here - if the fully constructed sub-object was a pointer, the pointer will be destroyed (the object that is pointed to is not a sub-object). However, a raw pointer has a trivial destructor.
> However, using auto_ptr and shared_ptr for members that needed new to be allocated
> made the process safer, since it destroys it anyway when it is no longer handled.
The mechanism is the same - a fully constructed member will be destroyed. The difference is that, unlike a raw pointer, the smart pointer has a non-trivial destructor.
A pointer pointing to a dynamically allocated object is a special case; rather than address each special case separately, address the general case. Acquire resources in constructors, and release them in destructors - no matter what the resource is. It may be an object that was allocated dynamically, it may be a file, it may be a network resource like a socket, it may be a lock on a database or a mutex ...
> the code becomes to cropped up with if-else and try-catch at the most unlikely places.