jakeva
jakeva

Reputation: 2835

Subclasses with different array implementations

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?

Upvotes: 0

Views: 212

Answers (1)

Michael Aaron Safyan
Michael Aaron Safyan

Reputation: 95509

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.

Upvotes: 3

Related Questions