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
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
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
(Bar*) **mybararray;
}
within Foo(){
mybararray = Foo*[rows][cols];
}
gives me the following error
cannot convert `
Foo* (*)[((cols - 1) + 1)]' to `Foo***' in assignment
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
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];
>>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]);
or
Bar * mybar = &(mybararray[1][2]);
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
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
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
ASKER
Cayce - your stuff looks pretty complete. Let me take some time to implement and digest that.
Thanks.
-Edward
Thanks.
-Edward
>mybararray[2][5] = new Bar(values);
new Bar() returns Bar *. What is the data type of mybararray[2][5]? Bar? Int?
new Bar() returns Bar *. What is the data type of mybararray[2][5]? Bar? Int?
ASKER
the data type of mybararray[2][5] should be Bar*.
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?
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.
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)?ne w T[row*col]:NULL){}
~dynamic_2d_array(){if(m_d ata) 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.
template < class T>
class dynamic_2d_array
{
public:
dynamic_2d_array(int row, int col):m_row(row),m_col(col)
~dynamic_2d_array(){if(m_d
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....
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[].
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
> 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<in t> > 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
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<in
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
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.
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.
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.
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
ASKER
Axter - your stuff was really good too I'll create another quesition for you for me to accept your answer.
- Edward
- 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 :)
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 :)
In which variations exactly?