I have a C++ class template that makes an Array of pointers. This also gets typedefed to make Arrays of Arrays and so on:
typedef Array<Elem> ElemArray;
typedef Array<ElemArray> ElemArrayArray;
typedef Array<ElemArrayArray> ElemArrayArrayArray;
I would like to be able to set one leaf node from another by copying the pointer so they both refer to the same Elem.
But I also want to be able to set one Array (or Array of Arrays etc) from another. In this case I don't want to copy the pointers, I want to keep the arrays seperate and descend into each one until I get to the leaf node, at where I finally copy the pointers.
I have code that does this (below). When you set something in an Array it calls a CopyIn method to do the copying.
But because this is templated it also has to call the CopyIn method on the leaf class, which means I have to add a dummy method to every leaf class that just returns false.
I have also tried adding a flag to the template to tell it whether it contains Arrays or not, and so whether to call the CopyIn method. This works fine - the CopyIn method of the leaf nodes never gets called, but it still has to be there for the compile to work!
Is there a better way to do this?
#include <stdio.h>
class Elem {
public:
Elem(int v) : mI(v) {}
void Print() { printf("%d\n",mI); }
bool CopyIn(Elem *v) { return false; }
int mI;
};
template < typename T > class Array {
public:
Array(int size) : mB(0), mN(size) {
mB = new T* [size];
for (int i=0; i<mN; i++)
mB[i] = new T(mN);
}
~Array() {
for (int i=0; i<mN; i++)
delete mB[i];
delete [] mB;
}
T* Get(int i) { return mB[i]; }
void Set(int i, T* v) {
if (! mB[i]->CopyIn(v) ) {
// its not an array, so copy the pointer
mB[i] = v;
}
}
bool CopyIn(Array<T>* v) {
for (int i=0; i<mN; i++) {
if (v && i < v->mN ) {
if ( ! mB[i]->CopyIn( v->mB[i] )) {
// its not an array, so copy the pointer
mB[i] = v->mB[i];
}
}
else {
mB[i] = 0;
}
}
return true; // we did the copy, no need to copy pointer
}
void Print() {
for (int i=0; i<mN; i++) {
printf("[%d] ",i);
mB[i]->Print();
}
}
private:
T **mB;
int mN;
};
typedef Array<Elem> ElemArray;
typedef Array<ElemArray> ElemArrayArray;
typedef Array<ElemArrayArray> ElemArrayArrayArray;
int main () {
ElemArrayArrayArray* a = new ElemArrayArrayArray(2);
ElemArrayArrayArray* b = new ElemArrayArrayArray(3);
// In this case I need to copy the pointer to the Elem into the ElemArrayArray
a->Get(0)->Get(0)->Set(0, b->Get(0)->Get(0)->Get(0));
// in this ca开发者_C百科se I need go down through a and b until I get the to Elems
// so I can copy the pointers
a->Set(1,b->Get(2));
b->Get(0)->Get(0)->Get(0)->mI = 42; // this will also set a[0,0,0]
b->Get(2)->Get(1)->Get(1)->mI = 96; // this will also set a[1,1,1]
// should be 42,2, 2,2, 3,3, 3,96
a->Print();
}
You could use template specialization to do something different with the leaf class:
template <> class Array<Elem> {
// Define how Array<Elem> should behave
}
As others mentioned in the comments, you should really consider using operator [] instead of Get and Set methods for array indexes.
I rearranged the code a bit to use template specialization, now the normal Set does a deep copy like this:
void Set(int i, T* v) { mB[i]->CopyIn(v); }
but for the array that contains the leaves you have to add the specialization:
template <> void Array<Elem>::Set(int i, Elem* v) { SetPtr(i,v); }
Of course you have to add a new one of these for every different leaf class. At least if you forget to do this you get a compiler error because it can't find CopyIn in the Elem class.
I thought about replacing CopyIn with an overloaded assignment operator, but this would loose the compile warning if you forgot to add the specialization.
I also thought about doing operator[] instead of Get and Set (just for this example), but it seems a bit dangerous with all these pointers flying around - it's easy to write a[0] instead of (*a)[0]. Also I couldn't work out the magic needed to convert the pointer in the Array into the correct sort of reference for [] to return (anyone know how to do that?).
#include <stdio.h>
class Elem {
public:
Elem(int v) : mI(v) {}
void Print() { printf("%d\n",mI); }
int mI;
};
template < typename T > class Array {
public:
Array(int size) : mB(0), mN(size) {
mB = new T* [size];
for (int i=0; i<mN; i++)
mB[i] = new T(mN);
}
~Array() {
for (int i=0; i<mN; i++)
delete mB[i];
delete [] mB;
}
T* Get(int i) { return (i<mN) ? mB[i] : 0; }
void Set(int i, T* v) { mB[i]->CopyIn(v); }
void SetPtr(int i, Elem* v) { mB[i] = v; }
bool CopyIn(Array<T>* v) {
for (int i=0; i<mN; i++) {
if (v && i < v->mN ) {
Set( i, v->Get(i) );
}
else {
mB[i] = 0;
}
}
}
void Print() {
for (int i=0; i<mN; i++) {
printf("[%d] ",i);
mB[i]->Print();
}
}
private:
T** mB;
int mN;
};
typedef Array<Elem> ElemArray;
typedef Array<ElemArray> ElemArrayArray;
typedef Array<ElemArrayArray> ElemArrayArrayArray;
template <> void Array<Elem>::Set(int i, Elem* v) { SetPtr(i,v); }
int main () {
ElemArrayArrayArray* a = new ElemArrayArrayArray(2);
ElemArrayArrayArray* b = new ElemArrayArrayArray(3);
// In this case I need to copy the pointer to the Elem into the ElemArrayArray
a->Get(0)->Get(0)->Set(0, b->Get(0)->Get(0)->Get(0));
// in this case I need go down through a and b until I get the to Elems
// so I can copy the pointers
a->Set(1,b->Get(2));
b->Get(0)->Get(0)->Get(0)->mI = 42; // this will also set a[0,0,0]
b->Get(2)->Get(1)->Get(1)->mI = 96; // this will also set a[1,1,1]
// should be 42,2, 2,2, 3,3, 3,96
a->Print();
}
精彩评论