0

Let's say I have a base class template MyBase:

template <class T>
class MyBase{
private:
    T data;
public:
    MyBase(T _data);
};

I want to subclass this twice (at least for now):

  1. data should be a dynamic 2-dimensional array: T **data
  2. data should be a fixed 2-dimensional array: T data[rows][cols]

I'm still a bit of a novice with C++, and I can't figure out how to do this. Specifically, I want to make a sort of matrix library (primarily as a learning project). I've done some things in the past where having my matrix stored dynamically made more sense, and vice versa. So, it seems like a good solution would be to implement a base class that provides all the common functionality (insert(T item, int i, int j), for example, should use data[i][j] = item; in either case), then subclass a DynamicMatrix and a FixedMatrix. The DynamicMatrix would have a constructor that did

data = new T*[rows];
for (int i = 0; i < rows; i++)
{
    data[i] = new T[cols];
}
for (int i = 0; i < rows; i++)
{
    for (int j = 0; j < cols; j++)
    {
         data[i][j] = 0;
    }
}

And FixedMatrix just:

for (i=0; i < rows; i++)
{
    for (j=0; j < cols; j++)
    {
         data[i][j] = 0;    
    }
}

Creating a member variable T data; in the base class is easy enough. But then in the subclasses, how do I convert that to a double pointer? Perhaps I can't, I'm okay with that. But then what should I do instead?

4
  • 2
    Can you explain in a bit more detail about what this base class represents, why you want to use inheritance here, and what trouble you're having? Commented Feb 10, 2011 at 5:43
  • sure, let me edit the question. Commented Feb 10, 2011 at 5:44
  • 1
    I think you are a little bit confused in terms of where things are allocated; this is "automatic storage", not necessarily "stack" storage. It will go wherever the containing object goes. If you use "new MyBase" to create the object, even if you do T[rows][cols] in the "MyBase" class, it will end up on the heap with the constructed "MyBase" object. Also, it is often desireable to allocate on the heap instead of the stack (e.g. the stack is often constrained in multithreaded programs, whereas the heap is shared across all threads and is usually not as bounded). Commented Feb 10, 2011 at 5:55
  • @Michael I knew calling it a stackmatrix would be controversial. I just couldn't think of another name. I see your point and agree, but nevertheless, I want to be able to allocate space on the stack (take it for granted the Matrix object as such would also be allocated on the stack). For example, a 4x4 matrix for use in OpenGL. Commented Feb 10, 2011 at 6:03

1 Answer 1

3

Here you are trying to use inheritance for code reuse which, in my opinion, is not a good approach to design; inheritance is for freedom of implementation while composition is for code reuse.

In this case, if it were really necessary to support these different cases, I would formalize a 2d array:

template<typename T> class Array2D {
    public:
       virtual const T* operator[](int row_index) const = 0;
       virtual T* operator[](int row_index) = 0;
       virtual size_t rows() const = 0;
       virtual size_t cols() const = 0;
};

Then I would provide the implementations of Array2D that you've specified:

template<typename T, int R, int C> class FixedArray2D : public Array2D {
     public:
       virtual const T* operator[](int row_index) const {
           return &data_[row_index][0];
       }
       virtual T* operator[](int row_index) {
            return &data_[row_index][0];
       }
       virtual size_t rows() const { return R; }
       virtual size_t cols() const { return C; }
     private:
        T data_[R][C];
};

template<typename T> class DynamicArray2D : public Array2D {
     public:
        DynamicAray2D(int rows, int cols) {
           // ...
        }
        // ...
};

At this point, you can instantiate an Array2D using either format. Now whatever code you are using can simply take a const Array2D& or an Array2D& wherever it needs to deal with such an object.

That said, I think this is probably unnecessary complexity in that a dynamically sized array will work for either case, hence I would just go with that unless there were a compelling reason to support both types.

Sign up to request clarification or add additional context in comments.

3 Comments

Can you explain your first sentence where you say 'composition is for code reuse'? This is the problem I'm trying to solve, currently I have two large classes, one for each kind of matrix, with >95% duplicate code.
Yes, basically you should strive to create types that contain (smart) pointers to other (possibly polymorphic types), and which add some additional functionality on top of the objects it contains. The constructor should allow you to pass in arbitrary subclasses of the needed types, so that the composed type can be reused with different configurations of the objects it contains.
Thanks for the tip. Funny to encounter such an apparently common design pattern for the first time- it's exactly what I was looking for.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.