A few basic questions about constructor & exception (in Managed C++ environment)

L

Lloyd Dupont

(not I use 2.0, so new return a "normal" pointer and gcnew return a managed
one, my question below regarding new concern plain standart C++ allocator)

- if I use the default new operator, are all the instance variable
initialize to NULL / 0 ?
- if there is not enough memory what happend with new ? does it return NULL
or throw an exception?
- if new throw a native C++ exception what happen in Managed C++ ?!
- if there is an exception in a construtor, is ~MyClass() called?
- if I want to throw a (managed) exception in a constructor should I free
all native
type before? or should I throw and let the destructor (which check nullity
before deleting) do its job?
 
T

Tamas Demjen

So I take your questions are native C++ related, except the last one.
- if I use the default new operator, are all the instance variable
initialize to NULL / 0 ?

Nothing is initialized by default. Just like malloc, the memory contains
random content. Unless new creates an object, whose constructor
initializes its members.
- if there is not enough memory what happend with new ? does it return NULL
or throw an exception?

throw std::bad_alloc.
- if new throw a native C++ exception what happen in Managed C++ ?!

C++ exceptions are not portable to other compilers (often not even
between different versions of the same vendor). Also, they're not
portable between different languages. I would say it's going to cause a
crash. You should never call an unmanaged function that throws from
managed code, always make sure to catch all exceptions and use a return
value:

bool UnmanagedFunction()
{
try
{
[...]
}
catch(...)
{
return false;
}
}

You have to do the same for C and COM wrappers.
- if there is an exception in a construtor, is ~MyClass() called?

No. If there's an exception in the constructor, the object is not
considered fully constructed, and therefore the destructor can't be
called. However, the destructors for its members that have already been
constructed are called.

So unless you use a smart pointer, you shouldn't throw from the
constructor before deallocating first. You should also uninitialize
everything before throwing. Try to pick up the habit of using smart
pointers and other RAII techniques. For example, when you open a file,
write a wrapper class whose destrucor performs the inverse operation
(closing the file). That way you're much safer.

You should never allow the destructor to throw. That can cause an
immediate crash under most cases. Your application may immediately
disappear from the task bar without any warning message.
- if I want to throw a (managed) exception in a constructor should I free
all native
type before? or should I throw and let the destructor (which check nullity
before deleting) do its job?

I don't know. There is no such thing as a desturctor in .NET, only the
C++/CLI language supports it, which is still in a draft stage. You can
easily confirm it by writing a test application, that's how I would try
it. You can try to find it in the draft standard. Maybe someone else can
answer that question for you. A good C++/CLI book would certainly be
very very useful *sight*.

Tom
 
L

Lloyd Dupont

thanks, very complete.
and annoying as well.. I mean the std::bad_alloc in case of not enough
memopry is a hassel... mmh... I have to think on that.
thanks!

--
There are 10 kinds of people in this world. Those who understand binary and
those who don't.
Tamas Demjen said:
So I take your questions are native C++ related, except the last one.
- if I use the default new operator, are all the instance variable
initialize to NULL / 0 ?

Nothing is initialized by default. Just like malloc, the memory contains
random content. Unless new creates an object, whose constructor
initializes its members.
- if there is not enough memory what happend with new ? does it return
NULL
or throw an exception?

throw std::bad_alloc.
- if new throw a native C++ exception what happen in Managed C++ ?!

C++ exceptions are not portable to other compilers (often not even between
different versions of the same vendor). Also, they're not portable between
different languages. I would say it's going to cause a crash. You should
never call an unmanaged function that throws from managed code, always
make sure to catch all exceptions and use a return value:

bool UnmanagedFunction()
{
try
{
[...]
}
catch(...)
{
return false;
}
}

You have to do the same for C and COM wrappers.
- if there is an exception in a construtor, is ~MyClass() called?

No. If there's an exception in the constructor, the object is not
considered fully constructed, and therefore the destructor can't be
called. However, the destructors for its members that have already been
constructed are called.

So unless you use a smart pointer, you shouldn't throw from the
constructor before deallocating first. You should also uninitialize
everything before throwing. Try to pick up the habit of using smart
pointers and other RAII techniques. For example, when you open a file,
write a wrapper class whose destrucor performs the inverse operation
(closing the file). That way you're much safer.

You should never allow the destructor to throw. That can cause an
immediate crash under most cases. Your application may immediately
disappear from the task bar without any warning message.
- if I want to throw a (managed) exception in a constructor should I free
all native
type before? or should I throw and let the destructor (which check
nullity
before deleting) do its job?

I don't know. There is no such thing as a desturctor in .NET, only the
C++/CLI language supports it, which is still in a draft stage. You can
easily confirm it by writing a test application, that's how I would try
it. You can try to find it in the draft standard. Maybe someone else can
answer that question for you. A good C++/CLI book would certainly be very
very useful *sight*.

Tom
 
N

Nemanja Trifunovic

std::bad_alloc is good, not a "hassel". However, if you don't like
exceptions, just use the "nothrow" version like this:

int* i = new(std::nothrow) int[50];

Just be sure to include <new>
 
L

Lloyd Dupont

std::bad_alloc is good, not a "hassel". However, if you don't like
I mean a NULL return seems better to me as I do afew new in my constructor.
so how could I escape memory leak in these condition and if the memory is not zeroed first?

I though of that (very verbose but probably very C++ way of doing things), what do you think?

// my managed not enoyugh memory exception!
static OutOfMemoryException^ NotEnoughMemory = gcnew OutOfMemoryException();

class MyClass
{
TYPE_1_T *var1, *var2, *var, *var4, *var5;
inline void SetNull()
{
var1 = NULL;
var2 = NULL;
var3 = NULL;
var4 = NULL;
var5 = NULL;
}
public:
MyClass()
{
// NULL them first, so I know what to delete
SetNull();
try
{
// some (memory) error throwing init code
var1 = new TYPE_1_T[SomeNumber];
// ... alloc/new other variable
}
catch
{
// here I delete all my allocated var, what do you think?
~MyClass();
throw NotEnoughMemory ;
}
}
~MyClass()
{
if(var1)
delete var1;
if(var2)
delete var2;
if(var3)
delete var3;
if(var4)
delete var4;
if(var5)
delete var5;

// just in case, I'm not really confident about C++
// could I have ~MyClass() private?
SetNull();
}
}
 
T

Tamas Demjen

First, you should really use delete [] instead of delete in this case.
Second, I wouldn't call the destructor directly. I recommend that you
introduce a Cleanup() function that you call from both the d'tor and the
c'tor.

But if you really want to know my opinion, I simply wouldn't use native
pointers at all. They're unsafe, from both the exception safety point of
view, and from the programmer mistakes point of view.

I would simply use std::vector in most cases. That's a reasonable
container. IF you know the number of items, you can use reserve to
pre-allocate memory for it. Instead of TYPE_1_T *, I would use
vector<TYPE_1_T>.

If that doesn't sound good, my second choice would be
boost::scoped_array, which is a smart pointer similar to auto_ptr, but
for arrays. It calls delete [] instead of delete to deallocate. The only
time I like scoped_array better than vector is when I need to allocate
uninitialized memory for extreme perforamance reasons. It doesn't happen
in typical application code, but in low-level library code, such as
image processing.

Both solutions are perfectly exception safe and you can't make a mistake
with them, and you don't have to worry about throwing from the construtor.

If I really had to use native unsafe pointer, I would consider calling
malloc and free directly, which don't throw.

Otherwise your code is working too, it's just cumbersome, difficult to
write, maintain and read, and it's easy to make a mistake there. For
example, if you later need to add a var6, you have to modify the code at
many different places, and if you just miss one of them, you'll get into
trouble. A good design is that requires the minimum amount of editing in
order to introduce an additional variable.

This is just an advice, don't feel bad about it, your solution basically
works (after changing delete to delete []).

Tom
 
A

adebaene

Lloyd Dupont a écrit :
I though of that (very verbose but probably very C++ way of doing things), what do you think?
This is a very "C" way of doing things, not very "C++" ;-)
// my managed not enoyugh memory exception!
static OutOfMemoryException^ NotEnoughMemory = gcnew OutOfMemoryException();

class MyClass
{
TYPE_1_T *var1, *var2, *var, *var4, *var5;
inline void SetNull()
{
var1 = NULL;
var2 = NULL;
var3 = NULL;
var4 = NULL;
var5 = NULL;
}
public:
MyClass()
{
// NULL them first, so I know what to delete
SetNull();
try
{
// some (memory) error throwing init code
var1 = new TYPE_1_T[SomeNumber];
// ... alloc/new other variable
}
catch
{
// here I delete all my allocated var, what do you think?
~MyClass();
throw NotEnoughMemory ;
}
}
~MyClass()
{
if(var1)
delete var1;
if(var2)
delete var2;
if(var3)
delete var3;
if(var4)
delete var4;
if(var5)
delete var5;

// just in case, I'm not really confident about C++
// could I have ~MyClass() private?
SetNull();
}
}

Your code is invalid because you call delete and not delete[] on a
new[] allocated array.
The C++ way of doing things would be to have :

class MyClass
{
std::vector<TYPE_1_T> var1, var2, var3, var4, var5;

public:
MyClass()
try //this is a little known construct : the "Function-try
block"
: var1(SomeNumber, var2(SomeOtherNumber) /*, .....*/
{}
catch(...)
{
/*not that you can't "swallow" an exception here, but you can
translate it to another exception type*/
throw NotEnoughMemory;
}
};

Note that you don't need to write a destructor or a "cleanup" function
at all! The compiler (and std::vector) will take care of all the
cleanup for you, wheter there is an exception or not.

See http://www.gotw.ca/gotw/066.htm for a complete discussion on the
subject. See also the "Deep C++" columns #14 to 16 in MSDN.

Arnaud
MVP - VC
 
L

Lloyd Dupont

I would simply use std::vector in most cases. That's a reasonable
container. IF you know the number of items, you can use reserve to
pre-allocate memory for it. Instead of TYPE_1_T *, I would use
vector<TYPE_1_T>.

but I need to call an API function which takes a
TYPE_1_T* (array)
could I use the std::vector<TYPE_1_T> and somehow get a TYPE_1_T* from it
when necessary (i.e. to call API function)?
 
L

Lloyd Dupont

let's say there is an C API function I want to call

void doSomething(TYPE_1_T* array, int len);

could I write a code like that:

std::vector<TYPE_1_T> var1(36);

// some computation, 36 is no good
var1 = std::vector<TYPE_1_T>(42);

doSomething(var1, 42);


sorry for the dummy question but I don't know the standart template library
at all.
instead, so far, I used new(nothrow) and check allocation and delete
everything manually, quite cumbersome...
 
A

adebaene

Lloyd Dupont a écrit :
let's say there is an C API function I want to call

void doSomething(TYPE_1_T* array, int len);

could I write a code like that:

std::vector<TYPE_1_T> var1(36);
Please note that var1 is filled here with 36 default initialized
TYPE_1_T.
// some computation, 36 is no good
var1 = std::vector<TYPE_1_T>(42);
var1.resize(42) would probably be better (since it will keep the
beginning of the vector intact), but your solution will work too.
doSomething(var1, 42);
doSomething(&var1[0], 42); is the correct way.
sorry for the dummy question but I don't know the standart template library
at all.
instead, so far, I used new(nothrow) and check allocation and delete
everything manually, quite cumbersome...
C++ is an object-oriented language, and OOP is about masking all the
cumbersome and pesky details inside a class, so that you don't need to
worry about it from the outside.

Arnaud
MVP - VC
 
T

Tamas Demjen

Lloyd said:
but I need to call an API function which takes a
TYPE_1_T* (array)
could I use the std::vector<TYPE_1_T> and somehow get a TYPE_1_T* from it
when necessary (i.e. to call API function)?

That's exactly what vector guarantees, the memory is allocated in a
contiguous block of memory, this way making it compatible with a C
array. You have two ways of getting a pointer to the first item:

vector<T> v;
T * ptr = &*v.begin();
or
T * ptr = &v[0];

Tom
 

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

Top