We help IT Professionals succeed at work.

Check out our new AWS podcast with Certified Expert, Phil Phillips! Listen to "How to Execute a Seamless AWS Migration" on EE or on your favorite podcast platform. Listen Now

x

Multidimensional array of pointers

edwardt
edwardt asked
on
Medium Priority
38,086 Views
Last Modified: 2013-12-14
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
Comment
Watch Question

jkr
CERTIFIED EXPERT
Top Expert 2012

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

In which variations exactly?

Author

Commented:
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

Author

Commented:
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
jkr
CERTIFIED EXPERT
Top Expert 2012

Commented:
>>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];


Commented:
Bar mybar = mybararray[1][2];

or

Bar * mybar = &(mybararray[1][2]);
CERTIFIED EXPERT
Commented:
Unlock this solution with a free trial preview.
(No credit card required)
Get Preview

Author

Commented:
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


Author

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

Thanks.

-Edward

Commented:
>mybararray[2][5] = new Bar(values);

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

Author

Commented:
the data type of mybararray[2][5] should be Bar*.

Author

Commented:

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?

Author

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

Commented:
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.
AxterSenior Software Engineer

Commented:
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.
AxterSenior Software Engineer

Commented:
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....
AxterSenior Software Engineer

Commented:
>>* 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[].
Top Expert 2004

Commented:
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
Top Expert 2004

Commented:
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

Author

Commented:
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
AxterSenior Software Engineer

Commented:
>>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.

Author

Commented:
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

Author

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

Commented:
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 :)
Unlock the solution to this question.
Thanks for using Experts Exchange.

Please provide your email to receive a free trial preview!

*This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

OR

Please enter a first name

Please enter a last name

8+ characters (letters, numbers, and a symbol)

By clicking, you agree to the Terms of Use and Privacy Policy.