Link to home
Start Free TrialLog in
Avatar of edwardt
edwardt

asked on

Multidimensional array of pointers

Hi,

I've been trying to allocate a multidimensional array of pointers, but have been unsuccessful as it seems that the multidimensional array examples have been of int, etc.

Lets say I have a class Foo and class Bar. I want to allocate a multidimensional array of Bar pointers

in class Foo{
Foo();
public:
  Bar *** mybararray;
}

in Foo(){

//initialize mybararray

}


so that eventually, I can do something like

Bar *myBar = mybararray[10][15];

But I've been having trouble, in that it seems that for different variations, my compiler complains that I pass back a "Bar" instead of "Bar*" when i do mybararray[10][15];

Thanks.

-Edward
Avatar of jkr
jkr
Flag of Germany image

>>But I've been having trouble, in that it seems that for different variations, my compiler complains

In which variations exactly?
Avatar of edwardt
edwardt

ASKER

Lets say within Foo {

(Bar*) **mybararray;

}

within Foo(){
mybararray = Foo*[rows][cols];

}

gives me the following error

cannot convert `
   Foo* (*)[((cols - 1) + 1)]' to `Foo***' in assignment
Avatar of edwardt

ASKER

I've also done things like declare

Bar **mybararray;

but when I do

Bar * mybar = mybararray[1][2];

The compiler complains that I can't return a Bar type into a Bar* type.

-Edward
>>but when I do
>>Bar * mybar = mybararray[1][2];
>>The compiler complains that I can't return a Bar type into a Bar* type.

And the compiler is right about that - you cannot do that. If you need a Bar*, you have to use

Bar * mybar = *mybararray[1][2];


Bar mybar = mybararray[1][2];

or

Bar * mybar = &(mybararray[1][2]);
ASKER CERTIFIED SOLUTION
Avatar of Cayce
Cayce
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of edwardt

ASKER

Ok,

I've changed it so that I can have

        mybararray = new Bar * [rows];

     Bar * h = &(mybararray[3][3]);

works.

If I do Bar * h = *mybararray[3][3]; it gives
"no match for `*
   Bar&' operator"


Now I am trying to assign a few entries, like
mybararray[2][5] = new Bar(values); but the compiler complains
" invalid   conversion from `Bar*' to `int' "

Any ideas? :)

Also, what would be the proper way to instantiate the array? I know that my rows = rows and cols = cols in the constructor.

Thanks

-Edward


Avatar of edwardt

ASKER

Cayce - your stuff looks pretty complete. Let me take some time to implement and digest that.

Thanks.

-Edward
>mybararray[2][5] = new Bar(values);

new Bar() returns Bar *. What is the data type of mybararray[2][5]? Bar? Int?
Avatar of edwardt

ASKER

the data type of mybararray[2][5] should be Bar*.
Avatar of edwardt

ASKER


Cayce, your code lets my rest of the code compile, except where I need to return a row of the array, lke

Bar * getRow( int i){
return mybararray[i];

}

It gives me
cannot convert `
   std::deque<Bar*, std::allocator<Bar*> >' to `
   Bar*' in return

as an error. Any work around?
Avatar of edwardt

ASKER

Ah - Cayce, I just used your C code instead and it compiles... i'll be testing it now...
Ed:

The C code should work flawlessy to return complete rows.

But you forgot a * on your function declaration:

Bar* getRow(int i)

should be:

Bar** getRow(int i) {

or

BarPtr* getRow(int i) {

Using typedefs is a very good practice.
I recommend you use either a vector<vector<Bar> > type class, or to use the following class:
template < class T>
class dynamic_2d_array
{
public:
    dynamic_2d_array(int row, int col):m_row(row),m_col(col), m_data((row!=0&&col!=0)?new T[row*col]:NULL){}
    ~dynamic_2d_array(){if(m_data) delete []m_data;}
    inline T* operator[](int i) {return (m_data + (m_col*i));}
    inline T const*const operator[](int i) const {return (m_data + (m_col*i));}
private:
    const int m_row;
    const int m_col;
    T* m_data;
};


You can use the above class in the following manner:
dynamic_2d_array<Bar> My_2d_Bar(10, 15);

Where 10 is the first dimension, and 15 the second dimension.
You can reference the above type just like a C style 2D array.
My_2d_Bar[2][11] = xyz;

This is much safer then trying to use new[] operator because you don't have to worry about possible memory leaks that could occur if you don't delete the array.
For your class, you can create it on the initialize list via following method.

class Foo{
Foo();
public:
  dynamic_2d_array<Bar> mybararray;
};

Foo::Foo()
:mybararray(10, 15) //Initialize variable here, between function name and the body of the function
{
}

Notice the full colon in the line the initialize list....
>>* C++ style, using dynamics structs - not really a good performer */

FYI:
If you use a vector<> class with iterators, it will out perform the C-Style array, both static and dynamic.

The std::vector class with iterators has a higher optimization then code using index operator[].
Hi edwardt,
> Using typedefs is a very good practice.

That's the trick with nested types. Just "typedef" your way around dimension by dimension:

typedef Bar* BarP;
typedef BarP* BarPArr;
typedef BarPArr* BarPArr2;

The good thing here is that you can replace this easily by some other solution later, such as vector:
typedef Bar* BarP;
typedef vector<BarP> BarPArr;
typedef vector<BarPArr> BarPArr2;

...and it's a lot more readable that Bar*** foo;

Cheers,
Stefan
edwardt,
Probably the easiest (and most "C++ish") way to solve this is a vector. Unfortunately, vectors don't grow automatically when you insert something new at a particular position, like they do in Perl, awk, or many other scripting languages. But that's just a matter of extending the vector template a little:


Here is an extension of the vector template which grows automatically like in Perl:

#include <vector>
#include <iostream>

using namespace std;



template<typename _Tp>
    class auto_vector : public vector<_Tp>
    {
         public:
         typedef _Tp               value_type;
         typedef value_type&       reference;
        typedef const value_type& const_reference;
        typedef size_t            size_type;
        explicit
        auto_vector()
        : vector<value_type>() { }
       
       
         reference
           operator[](size_type __n) {
                cout << "[" << __n << "] access. size=" << capacity();
                if(__n >= capacity()){
                       reserve((3*__n)/2 + 1); // Slop factor 1.5
                       cout << " -> vector grows to " << capacity();
                }
                cout << endl;
                return *(begin() + __n);
           }
           
           
        const_reference
        operator[](size_type __n) const {
                cout << "[" << __n << "] const access. size=" << capacity();
                if(__n >= capacity()){
                       reserve((3*__n)/2 + 1); // Slop factor 1.5
                       cout << " -> vector grows to " << capacity();
                }
                cout << endl;
             return *(begin() + __n);
        }    
        reference
        at(size_type __n) {return (*this)[__n]; }        
       
        const_reference
        at(size_type __n) const {return (*this)[__n]; }        
           
};
         

main(){
     auto_vector<auto_vector<int> > v2;
     
     v2[126][112]=32;
     cout << "v2[126][112]=" << v2[126][112] << endl;
     cout << "v2[126].capacity()=" << v2[126].capacity() << endl;
     cout << "v2[125].capacity()=" << v2[125].capacity() << endl;
}

...just remove the debug output lines once it's working and replace the in by BarP.

(probably the tons of typedefs at the beginning of the class definition are not necessary, as they're already definied in the parent class.)

Stefan
Avatar of edwardt

ASKER

Cayce,

How concerned should I be with memory leaks under your C version? I'm hoping I don't have to be considering to keep track of these arrays and continuously call delete.

Thanks.

-Edward
>>How concerned should I be with memory leaks under your C version? I'm hoping I don't have to be considering to keep track of these arrays and continuously call delete.

When ever you use dynamic memory with dumb pointers, you have to be concern with memory leaks.
That's why I would advice against using a pointer method.

Instead use a container, which is similar to a smart poitner, in that it does the clean up job for you.
The dynamic_2d_array class I previously posted will do that for you.

If you use vector class with static objects, it will also clean up the memory for you.

Using a vector<> with pointers is just as bad as using a C-Style 2D pointer, in that you have to make sure you delete all the objects.
Avatar of edwardt

ASKER

Thanks guys - at this point, I'll work with the original C style coding. If / when I realize some issues with memory leaks, I'll come back ;)-Edward
Avatar of edwardt

ASKER

Axter - your stuff was really good too I'll create another quesition for you for me to accept your answer.
- Edward
Edward,

    What I understand from your initial post is that you are trying to point an array (row) of bars which is a subset of mybararray. Since the mybararray has been declared with three stars (pointers), taking each star as a single array or row of elements, mybararray can be used/treated as a three dimensional arrays. As I see in your declaration

Bar *myBar = mybararray[10][15];

your intention is to point a row of values located at 10th row and 15 column in the triple dimension value array.  Your declaration should work, but some compiler which are not completely ASNI compatable may complain about it. Not to side track or put you in different direction, I suggest you to use the address of first element of that row, like

Bar *myBar = &mybararray[10][15][0];

This is same as your declaration, but for some reason, it does not work, you can this one for the same. But keep in mind, you are using pointers so you should also little bother about freeing the memory if you using it of heap.

Hope this helps. Good luck :)