Solved

Multidimensional array of pointers

Posted on 2004-04-07
23
37,994 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
0
Comment
Question by:edwardt
  • 10
  • 4
  • 2
  • +4
23 Comments
 
LVL 86

Expert Comment

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

In which variations exactly?
0
 

Author Comment

by:edwardt
Comment Utility
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
0
 

Author Comment

by:edwardt
Comment Utility
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
0
 
LVL 86

Expert Comment

by:jkr
Comment Utility
>>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];


0
 
LVL 23

Expert Comment

by:chensu
Comment Utility
Bar mybar = mybararray[1][2];

or

Bar * mybar = &(mybararray[1][2]);
0
 
LVL 9

Accepted Solution

by:
Cayce earned 500 total points
Comment Utility
I infer you're trying to create a Matrix of pointers to Bar.

So basically you need multidimensional arrays of (Bar*).

I think the best way is to go to dynamic structs, suchs as Vector or Deque.
If you don't want dynamic structs, I included the code for using normal ones at the end.

/* this typedef would help to easily understand the code for anyone */
typedef Bar* BarPtr;

/* C++ style, using dynamics structs - not really a good performer */

void foo() {
  std::deque< std::deque<BarPtr> > MyBarArray;

  const int XDIM = 9;
  const int YDIM = 17;
  MyBarArray.resize(XDIM);
  for(int x = 0; x < XDIM; x++)
  {
    MyBarArray[x].resize(YDIM);
    for(int y = 0; y < YDIM; y++)
      MyBarArray[x][y] = new Bar;
  }

  /* work with it */

  for(int x = 0; x < XDIM; x++)
    for(int y = 0; y < YDIM; y++)
      delete MyBarArray[x][y];
}

/* C-Style */

void foo() {
  BarPtr** MyBarArray = NULL;

  const int XDIM = 9;
  const int YDIM = 17;
  MyBarArray = (BarPtr**) malloc(sizeof(BarPtr*) * XDIM);
  for(int x = 0; x < XDIM; x++)
  {
    MyBarArray[x] = (BarPtr*) malloc(sizeof(BarPtr)* YDIM);
    for(int y = 0; y < YDIM; y++)
      MyBarArray[x][y] = new TObject;
  }

  /* work with it */

  for(int x = 0; x < XDIM; x++)
  {
    for(int y = 0; y < YDIM; y++)
      delete MyBarArray[x][y];
    free(MyBarArray[x]);
   }
   free(MyBarArray);

}
0
 

Author Comment

by:edwardt
Comment Utility
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


0
 

Author Comment

by:edwardt
Comment Utility
Cayce - your stuff looks pretty complete. Let me take some time to implement and digest that.

Thanks.

-Edward
0
 
LVL 23

Expert Comment

by:chensu
Comment Utility
>mybararray[2][5] = new Bar(values);

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

Author Comment

by:edwardt
Comment Utility
the data type of mybararray[2][5] should be Bar*.
0
 

Author Comment

by:edwardt
Comment Utility

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?
0
How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

 

Author Comment

by:edwardt
Comment Utility
Ah - Cayce, I just used your C code instead and it compiles... i'll be testing it now...
0
 
LVL 9

Expert Comment

by:Cayce
Comment Utility
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.
0
 
LVL 30

Expert Comment

by:Axter
Comment Utility
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.
0
 
LVL 30

Expert Comment

by:Axter
Comment Utility
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....
0
 
LVL 30

Expert Comment

by:Axter
Comment Utility
>>* 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[].
0
 
LVL 12

Expert Comment

by:stefan73
Comment Utility
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
0
 
LVL 12

Expert Comment

by:stefan73
Comment Utility
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
0
 

Author Comment

by:edwardt
Comment Utility
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
0
 
LVL 30

Expert Comment

by:Axter
Comment Utility
>>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.
0
 

Author Comment

by:edwardt
Comment Utility
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
0
 

Author Comment

by:edwardt
Comment Utility
Axter - your stuff was really good too I'll create another quesition for you for me to accept your answer.
- Edward
0
 
LVL 6

Expert Comment

by:SJT2003A
Comment Utility
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 :)
0

Featured Post

Highfive Gives IT Their Time Back

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

Join & Write a Comment

Introduction This article is the first in a series of articles about the C/C++ Visual Studio Express debugger.  It provides a quick start guide in using the debugger. Part 2 focuses on additional topics in breakpoints.  Lastly, Part 3 focuses on th…
Basic understanding on "OO- Object Orientation" is needed for designing a logical solution to solve a problem. Basic OOAD is a prerequisite for a coder to ensure that they follow the basic design of OO. This would help developers to understand the b…
THe viewer will learn how to use NetBeans IDE 8.0 for Windows to perform CRUD operations on a MySql database.
The goal of the tutorial is to teach the user how to use functions in C++. The video will cover how to define functions, how to call functions and how to create functions prototypes. Microsoft Visual C++ 2010 Express will be used as a text editor an…

743 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

15 Experts available now in Live!

Get 1:1 Help Now