Destroying an array of arrays

O

Olumide

Hello -

I have two classes A and B as follows:

class B{
public:
~B(){ cout << "destroying B" << endl; }
};

class A{
public:
~A(){ cout << "destroying A" << endl; }
B** b;
};

When used in my main function as follows:

int main()
{
A a;
a.b = new B*[4];

for(int i = 0; i < 3; i++)
{
a.b = new B;
}

}

Only the destructor of A is called.

<ramble>
Do I need to wrap B in an intermediate class before it can be
destroyed? I sort of have the idea that destructors are not called
over(?) a pointer
</ramble>

Thanks,

- Olumide
 
S

Salt_Peter

Olumide said:
Hello -

I have two classes A and B as follows:

class B{
public:
~B(){ cout << "destroying B" << endl; }
};

class A{
public:
~A(){ cout << "destroying A" << endl; }
B** b;
};

When used in my main function as follows:

int main()
{
A a;
a.b = new B*[4];

for(int i = 0; i < 3; i++)
{
a.b = new B;
}

}

Only the destructor of A is called.


Well yes, the local copy of A is destroyed at the end of the scope it
is in since its a local variable.
If you allocate anything with new, it's your responsability to
deallocate with delete, or as in this case, delete [].
<ramble>
Do I need to wrap B in an intermediate class before it can be
destroyed? I sort of have the idea that destructors are not called
over(?) a pointer
</ramble>

What? the pointer is used to track the allocations you are required to
deallocate manually. The compiler has no way to deallocate since new
removes *all* responsability that the compiler has to free up those
array blocks.

Why not use std::vector< std::vector > to do this? You wouldn't need to
allocate anything on the heap. And if really want to use pointers, then
do without the B** indirection. Pointers are a pain enough as it is.

Consider using templates as follows in the case you prefer primitive
arrays.

#include <iostream>

class B {
public:
B() { std::cout << "B()\n"; }
~B() { std::cout << "~B()\n"; }
};

template< typename T, // element type
const size_t Arrays, // number of arrays
const size_t Size > // size of each array
class A {
T* array[Arrays];
public:
A() { std::cout << "A()\n"; }
~A() { std::cout << "~A()\n"; }
void allocate()
{
for(size_t i = 0; i < Arrays; ++i)
{
array = new T[Size];
}
}
void deallocate()
{
for(size_t i = 0; i < Arrays; ++i)
{
delete [] array;
}
}
};

int main()
{
A< B , 2, 2 > a; // 2 arrays of 2 elements each
a.allocate();
a.deallocate();
}

/*
A()
B()
B()
B()
B() // thats 2 x 2 = 4
~B()
~B()
~B()
~B()
~A()
*/

Note that T* array[Arrays] is created as an array of pointers to
nothing. That array gets zapped along with the a variable in main. So
once you get the above working correctly, you can stick allocate() in
ctor and deallocate() in d~tor.
 
G

Grizlyk

Olumide said:
class A{
public:
~A(){ cout << "destroying A" << endl; }
B** b;
};
...
int main()
{
A a;
...
Only the destructor of A is called.

The object "a" of the class "A" has auto storage modifier and C++
comipler manages memory of auto objects (calls its destructors) and
places the objects to stack. Your class A has the "b" member of the
class "B**"(pointer to pointer to B). The class "B**" is not the same
as the class "B" (predefined class "B**" has no destructor "{ cout <<
"destroying B**" << endl; }" ) and deleting the pointer not forced
deleting an object, which it point.

You need destructor of A like this:
private:
enum { arr_size=4 };
do_destruct()throw()
{
for( int i = 0; i<(arr_size-1) ; ++i ) { delete a.b; a.b
= 0; }
delete[] b; b=0;
}

public:
~A(){
do_destruct();
cout << "destroying A" << endl;
}

In the class A you use a C-style arrays. It is nothing wrong "in
general", but you _must not_ allow external memory allocation for the
class.

Memory management (and mostl other management) must be incapsulated by
your class A and details of memory allocation must be hidden by special
part of class interface, with the help of some methods, for instance,
with the help of std methods - constructor and destructor or by your
own virtual methods - create()/destroy().

This is no good
a.b = new B*[4];

for(int i = 0; i < 3; i++)
{
a.b = new B;
}


You can do like this
enum { arr_size=4 };
A()throw(exception&):b(0)
{
b = new B*[arr_size];
for(int i = 0; i<(arr_size-1) ; ++i) b=0; //it is realy easy
to foget

try{ for(int i = 0; i < (arr_size-1); ++i) b = new B;
}catch(...) { do_destruct(); throw; }
}

If you have STL implementet for you C++ compiler and target, you can
use vector<> and so on insted of C-style arrays. You can also make you
own light class "my_array" instead of vector<>, in order to place code
of memory allocation methods in one class.
 
G

Grizlyk

Oops.
do_destruct()throw()
{
for( int i = 0; i<(arr_size-1) ; ++i ) { delete a.b; a.b = 0; }
delete[] b; b=0;
}

I have written a hard error, much better
do_destruct()throw()
{
if(b) for( int i = 0; i<(arr_size-1) ; ++i ) { delete b; b = 0;
}
delete[] b; b=0;
}

The error tells us that we must be very accurate especially while using
C-style arrays, else hard errors can appear and that any class, already
have encapsuleted memory allocation, is much suitable instead of direct
C-style arrays control.
 
G

Grizlyk

Grizlyk said:
for( int i = 0; i<(arr_size-1) ; ++i ) { delete a.b; a.b = 0; }


And error again.

One kind man told me, that I have done an error again. It is realy
unpleasant error in "for" condition, must be
for(int i=0; i<arr_size; ++i)

It is often better to use true name insted of "arr_size", for example:
"rooms", "my_cars" and so on, but it can be _not_ more easy to check
the type of erorrs with the help of the name .

There are too many simplest errors in the short piece of code i have
done, but it would be better to correct them :).
 

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
474,432
Messages
2,571,682
Members
48,796
Latest member
Greg L.

Latest Threads

Top