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):
data should be a dynamic 2-dimensional array: T **data
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?
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.
Related
I have been working on a matrix library project similar to the vector library and am currently stuck at iterators. I have been trying to implement a column iterator for quite some time without the use of other libraries (besides iostream and fstream) and my workaround was to have a **columnVector that contains the references of a specified column when colBegin(i) or colEnd(i) is called. The implementation works but I can't manage to make it work aesthetically without going in myMatrix.columnVector as I want it to be encapsulated, rather I want to call matrix::iterator it = myMatrix.colBegin(i) and to iterate with addition and subtraction but my iterator subclass has only a 1d pointer (for the row iterator, hope I can use it for the column iterator as well).
Do you have an idea how to make a column iterator for a dynamically allocated 2d array?
Example of code (trying to keep it as short as possible):
using ll = unsigned long long;
template <typename Type> class matrix
{
// Iterator
public:
class iterator
{
protected:
Type* pointer;
public:
iterator() : pointer(nullptr) {}
iterator(Type* ptr);
Type& operator * ();
...
};
private:
ll numberOfColumns, numberOfRows;
Type** data;
public: // temporary, wish for it to be private
Type** columnVector;
public:
matrix() : numberOfColumns(NULL), numberOfRows(NULL), data(nullptr), columnVector(nullptr) {}
matrix(const ll, const ll);
~matrix();
Type*& operator [] (const ll value);
Type** colBegin(const ll index);
Type** colEnd(const ll index);
...
};
// Thought the constructor might be important since it allocates memory for columnVector as well
template <typename Type> matrix<Type> ::matrix(const ll _numberOfColumns, const ll _numberOfRows)
{
// Copy _numberOfColumns and _numberOfRows into this
numberOfColumns = _numberOfColumns;
numberOfRows = _numberOfRows;
// Allocates memory for data matrix
other.data = (Type**) new Type * [numberOfColumns];
for (size_t i = 0; i < numberOfColumns; i++)
other.data[i] = (Type*) new Type[numberOfRows];
// Fills data matrix with 0
for (size_t i = 0; i < other.numberOfColumns; i++)
for (size_t j = 0; j < other.numberOfRows; j++)
other.data[i][j] = 0;
// Allocation of space for columnVector
columnVector = new Type * [numberOfRows];
}
template <typename Type> Type** matrix<Type> ::colBegin(const ll index)
{
for (size_t i = 0; i < numberOfRows; i++)
columnVector[i] = &data[i][index];
return columnVector;
}
template <typename Type> Type** matrix<Type> ::colEnd(const ll index)
{
for (size_t i = 0; i < numberOfRows; i++)
columnVector[i] = &data[i][index];
return (columnVector + numberOfRows);
}
You don't even need an iterator. Each column is represented with an array. To iterate this array you need to know its beginning and the address of the element after the last one. So the Type* can represent you the iterator:
template <typename Type> class matrix
{
public:
using column_iterator = Type*;
};
So the colBegin/colEnd methods should look like that:
template<typename Type>
matrix<Type>::column_iterator matrix<Type>::colBegin(const ll index) {
return data[index];
}
template<typename Type>
matrix<Type>::column_iterator matrix<Type>::colEnd(const ll index) {
return data[index] + numberOfRows;
}
If you find that the code above iterates throught the row (which is not the case, as your contructor clearly describes data[i] as a column), you may define a class that represents an iterator:
class row_iterator {
public:
row_iterator(Type** data,
size_t rowIndex,
size_t columnIndex)
: m_data(data), m_rowIndex(rowIndex), m_columnIndex(columnIndex) {
}
bool operator == (const row_iterator &other) const {
return other.data == m_data &&
other.m_rowIndex == m_rowIndex &&
other.m_columnIndex == m_columnIndex;
}
row_iterator& operator ++() {
++m_columnIndex;
return *this;
}
row_iterator& operator ++(int) {
row_iterator temp = *this;
++*this;
return temp;
}
Type& operator *() const {
return m_data[m_columnIndex][m_rowIndex];
}
// other operators
private:
Type** m_data;
size_t m_rowIndex;
size_t m_columnIndex;
};
To use this iterator with the container you need to define:
row_iterator rowBegin(size_t rowIndex) {
return row_iterator(data, rowIndex, 0);
}
row_iterator rowEnd(size_t rowIndex) {
return row_iterator(rowIndex, rowIndex, numberOfColumns);
}
I am trying to implement a templated class that has a vector container from the standard library as a member. The purpose of the class is to create a linear algebra class for my personal use.
The class is supposed to have a constructor that initializes a vector filled only with zeros and two overloadings of the [] operator to access the data.
The code looks as follows:
#include <vector>
#include <iostream>
template<class T>class LinAlg
{
private:
std::vector<T> mVector;
int mSize;
public:
LinAlg(int size);
T & operator[](int i);
T const & operator[] (int i)const;
};
template<class T> LinAlg<T>::LinAlg(int size)
{
mSize = size;
std::vector<T> mVector;
for (int i=0; i<mSize; i++)
{
mVector.push_back(0);
}
}
template<class T> T & LinAlg<T>::operator[](int i)
{
return mVector[i];
}
template<class T> T const& LinAlg<T>::operator[](int i)const
{
return mVector[i];
}
int main()
{
LinAlg<double> vec(2);
vec[0] = 1.0;
vec[1] = 1.0;
for(int i=0; i<2; i++)
{
std::cout << vec[i] << '\n';
}
return 0;
}
Apparently, the code compiles correctly, but there is no output from the main function. The problem appears to be in the overloading of the operators and I have been reading in different forums but I haven't been able to find the solution.
Any help is appreciated.
In
template<class T> LinAlg<T>::LinAlg(int size)
{
mSize = size;
std::vector<T> mVector; // <- oops
for (int i=0; i<mSize; i++)
{
mVector.push_back(0);
}
}
std::vector<T> mVector; defines a new variable mVector that shadows the member variable mVector. The local mVector effectively replaces the member mVector and is initialized, loaded with 0s and then discarded when the constructor ends. The member mVector is left default initialized to size zero, causing problems later on when you attempt to access the values you believe you stored.
Instead you could
template<class T> LinAlg<T>::LinAlg(int size)
{
mSize = size;
// std::vector<T> mVector; <- remove this
for (int i=0; i<mSize; i++)
{
mVector.push_back(0);
}
}
but std::vector has a constructor that does this work for you, default initializing size elements, that you can take advantage of in the Member Initializer List
template<class T> LinAlg<T>::LinAlg(int size):
mVector(size), mSize(size)
{
}
Further, since std::vector knows its length, you can remove the mSize member and replace uses of it with mVector.size()
In your constructor, you are declaring a local variable mVector that hides the class member of the same name. So you are populating the local variable, not the class member. You need to remove the local variable:
template<class T> LinAlg<T>::LinAlg(int size)
{
mSize = size;
for (int i=0; i<mSize; i++)
{
mVector.push_back(0);
}
}
Now, that being said, you don't need the loop at all, since std::vector has a resize() method that can fill the vector with values:
template<class T> LinAlg<T>::LinAlg(int size)
{
mSize = size;
mVector.resize(size); // <-- will set new elements to 0 by default
}
Alternative, you can use the vector's own constructor instead, which does the same thing:
template<class T> LinAlg<T>::LinAlg(int size)
: mVector(size) // <-- will set elements to 0 by default
{
mSize = size;
}
Either way, the mSize member is redundant, as std::vector has its own size() method that you can use when needed.
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 6 years ago.
Improve this question
I was giving a adventurer class containing a bunch of functions and member variables.
One of them is:
string*** items;
so first I thought it was a 3d array that I have to make but I was told that it suppose to be a pointer to a 2d array.
What I tried to do was make a temp array
string** temp;
Init that and fill it, I then point my items to temp
items = &temp;
This works till the function exits. Then we I try and call a index value inside items
cout<<*item[0][0];
there is nothing. When temp disappears so does the array.
This line also doenst work
(*items) = new string*[2];
I couldn't find anything online that helped me.
How can I initialize items or keep the array data that i made using temp.
For those asking for the code, this is what they gave me:
class Adventurer{
private:
string*** items;
string name;
double maxCarryWeight;
double currentCarryWeight;
int currentNumberOfItems;
int maxNumberOfItems;
double health;
static int numberOfAdventurers;
public:
Adventurer(); //default constructor
Adventurer(const Adventurer& a); //copy constructor
~Adventurer();
bool pickUpItem(string it, double weight);
bool dropItem(string it);
bool dropItem(int index);
void setName(string n);
string getName() const;
void setMaxCarryWeight(double w);
double getMaxCarryWeight() const;
void setCurrentCarryWeight(double w);
double getCurrentCarryWeight() const;
void setMaxNumberOfItems(int n);
int getMaxNumberOfItems() const;
void setCurrentNumberOfItems(int n);
int getCurrentNumberOfItems() const;
int getNumberOfAdventurers() const;
void setHealth(double h);
double getHealth() const;
string** getItem(int index) const;
Adventurer& operator = (const Adventurer& a);
};
And said that
string*** items;
is a pointer to a 2d array
It's not entirely clear what you are trying to achieve from your question but it seems your main issue is likely to do with returning the address of a local variable when attempting to allocate your 2D C-style std::string array. Below is a very basic example of how to avoid such an issue via returning the allocated 2D array and then taking the address of this returned value and storing it in your std::string*** items variable.
// allocate memory for a 2D C-style array of std::string's
std::string** allocate_2d_array(std::size_t rows, std::size_t cols) {
std::string** items_arr = new std::string*[rows];
for (std::size_t i = 0; i < rows; ++i)
items_arr[i] = new std::string[cols];
return items_arr;
}
// print each element of the 2D C-style array via a pointer to the array
void print_items(std::ostream& os, std::string*** items, std::size_t rows, std::size_t cols) {
for (std::size_t i = 0; i < rows; ++i) {
for (std::size_t j = 0; j < cols; ++j)
os << (*items)[i][j] << ' ';
os << '\n';
}
}
// destruct the 2D C-style array
void deallocate_2d_array(std::string** items_arr, std::size_t rows, std::size_t cols) {
for (std::size_t i = 0; i < rows; ++i)
delete[] items_arr[i];
delete[] items_arr;
}
int main(void) {
std::size_t rows = 3; // matrix rows
std::size_t cols = 3; // matrix columns
// allocate a 2D array of std::string's
std::string** items_arr = allocate_2d_array(items, 3, 3);
// set the pointer to a 2D std::string array to address of items_arr
std::string*** items = &items_arr;
int count = 0;
// fill items_arr with data via items pointer
for (std::size_t i = 0; i < rows; ++i) {
for (std::size_t j = 0; j < cols; ++j)
(*items)[i][j] = std::to_string(++count);
}
print_items(std::cout, items); // print matrix to terminal
deallocate_2d_array(items_arr, rows, cols); // deallocate items_arr
}
However, as mentioned in the comments, this is not in keeping with modern c++ and one would much rather use a std::vector<std::vector<std::string>> to store a matrix of std::string instances.
You mentioned that using std::vector is not an option but I suspect that your teacher probably didn't say anything about making your own barebones dynamic array with similar semantics to a std::vector so that is always one way around these silly restrictions. With that in mind, below is the framework for a very basic (and untested) class which mimics a std::vector (without using allocators) which would make your task much simpler.
template<typename Ty>
class dynamic_array {
public:
typedef Ty value_type;
typedef Ty& reference;
typedef const Ty& const_reference;
typedef Ty* pointer;
typedef const Ty* const_pointer;
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;
// CONSTRUCTION/ASSIGNMENT
dynamic_array()
: arr_capacity(0), arr_size(0) {}
dynamic_array(size_type count)
: arr_capacity(count), arr_size(count) { allocate(count); }
~dynamic_array() { destroy(); }
dynamic_array& operator=(dynamic_array _other) {
swap(*this, _other);
return *this;
}
// CAPACITY
bool empty() const noexcept { return arr_size; }
size_type size() const noexcept { return arr_size; }
size_type capacity() const noexcept { return arr_capacity; }
void reserve(size_type new_cap) { if (new_cap > arr_capacity) reallocate(new_cap); }
// ELEMENT ACCESS
reference operator[](size_type n) { return arr[n]; }
const_reference operator[](size_type n) const { return arr[n]; }
// MODIFIERS
void clear() {
for (size_type i = 0; i < arr_size; ++i)
(&arr[i])->~value_type();
arr_size = 0;
}
void push_back(const value_type& _val) {
if (arr_size == arr_capacity) // TODO: expand arr using reallocate
pointer val = new (arr + arr_size) value_type(_val);
++arr_size;
}
void pop_back() {
(&arr[arr_size-1])->~value_type();
--arr_size;
}
void swap(dynamic_array& _other) {
std::swap(arr, _other.arr);
std::swap(arr_capacity, _other.arr_capacity);
std::swap(arr_size, _other.arr_size);
}
static void swap(dynamic_array& lhs, dynamic_array& rhs) { lhs.swap(rhs); }
private:
value_type* arr;
size_type arr_capacity;
size_type arr_size;
void allocate(size_type n) { arr = new value_type[n]; }
void reallocate(size_type new_cap) {
value_type* tmp = new value_type[new_cap];
size_type tmp_rows = (new_cap > arr_capacity) ? arr_capacity : new_cap;
for (size_type i = 0; i < tmp_rows; ++i)
tmp[i] = std::move(arr[i]);
delete[] arr;
arr = tmp;
arr_capacity = new_cap;
}
void destroy { clear(); delete[] arr; }
};
Then instead of messing around with lots of raw pointers and the headaches they bring, you can just pass around your dynamic_array<dynamic_array<std::string>> class instance without needing to worry about memory management.
Note: The above dynamic_array class is untested and probably requires some tweaks, it is also not a great example of implementing a STL-style container (you'd need allocator and iterator support), it is just intended as a barebones std::vector mimicking container to get around the "no vector" task requirement.
I have a class template
template <class T> class Collection
{
private:
int size;
int type;
T* Arr;
int Case;
public:
void ArrayGenerating() {
switch(type) {
case 1:
Arr = new T[size];
for (int i = 0; i < size; i++) {
srand((unsigned)time(NULL));
Arr[i] = static_cast <T> (rand()) % size;
}
case 2:
Arr = new T[size];
for (int i = 0; i < size; i++) {
srand((unsigned)time(NULL));
Arr[i] = static_cast <T> (rand()) / (static_cast <T> (RAND_MAX/size));
}
case 3:
Arr = new T[size];
for (int i = 0; i < size; i++) {
srand((unsigned)time(NULL));
Arr[i].setNumerator(static_cast <int> (rand()) % size);
srand((unsigned)time(NULL));
Arr[i].setDenominator(static_cast <int> (rand()) % size);
}
}
}
};
I want to creat an random array of generic data type
with type 1, that is an interger array. type 2, an float array. type 3, I have an self-defined data type "fraction". But when I compile the programm, there are errors:
Error 1 error C2228: left of '.setNumerator' must have class/struct/union
Error 2 error C2228: left of '.setDenominator' must have class/struct/union
So if there are any solution for this complication?
I guess, type is a constant depending on T. Otherwise it would make no sense to have a T* point to an int, when T is a float. If that is true, it is not necessary at all.
I think, what you are looking for is template specialization (untested code):
// this is common to all cases.
class CollectionBase {
protected:
int size;
};
// the general template is not defined
// the compiler will complain whenever T is neither int, nor float, nor fraction.
template<class T> class Collection;
// here come the specializations
template<> class Collection<int>: private CollectionBase
{
private:
int* Arr;
public:
void ArrayGenerating() {
Arr = new int[size];
for (int i = 0; i < size; i++) {
srand((unsigned)time(NULL));
Arr[i] = static_cast<int>(rand()) % size;
}
}
};
template<> class Collection<float>: private CollectionBase
{
private:
float* Arr;
public:
void ArrayGenerating() {
Arr = new float[size];
for (int i = 0; i < size; i++) {
srand((unsigned)time(NULL));
Arr[i] = static_cast<float>(rand()) / (static_cast<float>(RAND_MAX/size));
}
}
};
template<> class Collection<fraction>: private CollectionBase
{
private:
fraction* Arr;
public:
void ArrayGenerating() {
Arr = new fraction[size];
for (int i = 0; i < size; i++) {
srand((unsigned)time(NULL));
Arr[i].setNumerator(static_cast <int> (rand()) % size);
srand((unsigned)time(NULL));
Arr[i].setDenominator(static_cast <int> (rand()) % size);
}
}
};
Please note, that this kind of code is dangerous. Consider std::vector<> instead of managing dynamically allocated array yourself.
Also be aware, that as a rule of thumb all methods of your class should be safely callable as soon as the constructor has finished. In your code any function that accesses Arr uses a random pointer to some memory, before ArrayGenerating() has run. Whenever you call ArrayGenerating() twice for some reason, your code will leak memory, because you never bother to delete[] your array before creating a new one.
The best tool C++ gives you for memory management is constructors and destructors. You are best of, when you encapsulate every resource, that you have to release once in a while, in a handler object. In this case std::vector already does what you need.
So here is a full (yet untested) most generic solution for you. I'd start with a free function to create random numbers:
template<typename T> struct dist{
using uniform = std::uniuniform_int_distribution<T>;
};
template<> struct dist<float> {
using uniform = std::uniuniform_real_distribution<float>;
};
template<typename T>
std::vector<T> createRandomNumbers(size_t s) {
auto e1 = std::default_random_engine{std::random_device{}()};
auto u = dist<T>::uniform{0, static_cast<T>(s)};
auto r = std::vector<T>(s, 0);
for( auto& i: r ) i = u(e1);
return r;
}
// fraction need a specialization
template<>
std::vector<fraction> createRandomNumbers<fraction>(size_t s) {
auto e1 = std::default_random_engine{std::random_device{}()};
auto u = dist<int>::uniform{0, static_cast<int>(s)};
auto r = std::vector<fraction>(s, 0);
for( auto& i: r ) {
i.setNumerator(u(e1));
i.setDenominator(u(e1));
}
return r;
}
Now we implement a Collection class template like yours, if we really still need it:
template <typename T> Collection {
private:
// this will handle all your memory management needs
std::vector<T> randoms;
public:
Collection(size_t s) :
randoms{createRandomNumbers<T>(s)}
{};
createNewRandoms(size_t s) {
std::swap(randoms, createRandomNumbers<T>(s));
};
// whatever else is necessary
};
Why would you want to do this and make your life infinitely more difficult?
It could be as simple as this:
#include <iostream>
#include <chrono>
#include <random>
template<class type_t, std::size_t size>
class Array
{
private:
type_t arr[size];
public:
Array()
{
for (std::size_t i = 0; i < size; ++i)
{
//nice C++ random number generation
auto seed = static_cast<unsigned>(std::chrono::system_clock::now().time_since_epoch().count());
std::minstd_rand0 randm(seed);
arr[i] = randm();
}
}
//test function
void print()
{
for (int i = 0; i < size; ++i)
std::cout << arr[i] << " ";
}
};
int main()
{
Array<int, 4> arr;
arr.print();
std::cin.get();
}
Try to get away from C-style C++. Join the dark side.
Note: I won't comment on your use of C functions or other problems. Others have already told you how to avoid them.
A type member in a class template somehow defeats the purpose of generic programming, doesn't it? You should get rid of your type and replace the switch with template specialisation.
Here is a simple example to get you started:
// Collection for all T except of `fraction`
template <class T> class Collection
{
private:
int size;
T* Arr;
int Case;
public:
void ArrayGenerating() {
Arr = new T[size];
for (int i = 0; i < size; i++) {
srand((unsigned)time(NULL));
Arr[i] = static_cast <T> (rand()) % size;
}
};
};
// Collection for `fraction`
template <> class Collection<fraction>
{
private:
int size;
fraction* Arr;
int Case;
public:
void ArrayGenerating() {
Arr = new fraction[size];
for (int i = 0; i < size; i++) {
srand((unsigned)time(NULL));
Arr[i].setNumerator(static_cast <int> (rand()) % size);
srand((unsigned)time(NULL));
Arr[i].setDenominator(static_cast <int> (rand()) % size);
}
}
};
This is the simplest kind of template specialisation but may lead to a lot of code duplication. You can get around this problem, too, though. For example, you could extract all common parts into a common private base class, something like this:
namespace detail
{
template <class T> class CollectionBase
{
protected:
int size;
T* Arr;
int Case;
};
}
template <class T> class Collection : detail::CollectionBase<T>
{
public:
void ArrayGenerating() {
Base::Arr = new T[Base::size];
for (int i = 0; i < Base::size; i++) {
srand((unsigned)time(NULL));
Base::Arr[i] = static_cast <T> (rand()) % Base::size;
}
};
private:
using Base = detail::CollectionBase<T>;
};
template<> class Collection<fraction> : detail::CollectionBase<fraction>
{
public:
void ArrayGenerating() {
Base::Arr = new fraction[Base::size];
for (int i = 0; i < size; i++) {
srand((unsigned)time(NULL));
Arr[i].setNumerator(static_cast <int> (rand()) % size);
srand((unsigned)time(NULL));
Arr[i].setDenominator(static_cast <int> (rand()) % size);
}
}
private:
using Base = detail::CollectionBase<fraction>;
};
Generally, read more about template specialisation and you will certainly find the right solution:
http://en.cppreference.com/w/cpp/language/template_specialization
http://en.cppreference.com/w/cpp/language/partial_specialization
Some days ago I wrote a template class which looks like this:
template <class T>
class Matrix {
private:
T ** matrix;
int n;
int m;
.
.
.
}
And constructor:
template <class T>
Matrix<T>::Matrix(int _n, int _m)
{
n = _n;
m = _m;
matrix = new T * [n];
for (int i = 0; i < n; i ++)
{
matrix[i] = new T[m];
}
}
And right now I would like to create a destructor like this:
template <class T>
aghMatrix<T>::~aghMatrix()
{
for (int i = 0; i < n; i ++)
{
delete [] matrix[i];
}
}
At first I thought it'd solve all problems but I was wrong.
In function main:
Matrix<int> a; //destructor works find
Matrix<int*>a; //error destructor
How to solve this problem?
I will assume that this is a coding exercise of some kind. For a real world application you should not write your own matrix implementations but rather rely on containers found in the standard library or on one of the many matrix libraries out there.
The example that was posted had several problems and wouldn't even compile. So let me just start by giving a minimal solution:
template <class T>
class Matrix {
public:
Matrix(int _n, int _m)
: n(_n), m(_m), matrix(new T*[n]) // always initialize your members
{
for (int i=0; i<n; i++)
{
matrix[i] = new T[m];
}
}
~Matrix()
{
for (int i=0; i<n; i++)
{
delete[] matrix[i];
}
delete[] matrix; // this deletes the first level
}
private:
int n;
int m;
T ** matrix;
};
What should be learnt from this is:
Make sure you initialize every class member in the constructor's
initializer list for safety and performance reasons
Do not forget to delete the memory pointed to by your first level pointers (last line in destructor)
Do not use explicit new and delete if you don't have to
What about copy and move semantics? They are defined but won't do what they should.
In order to make use of RAII and to avoid explicit delete you can use std::unique_ptr instead. The equivalent code now looks like this:
#include <memory>
template <class T>
class Matrix2 {
public:
Matrix2(int _n, int _m)
: n(_n), m(_m), matrix(new T[n * m])
{}
private:
int n;
int m;
std::unique_ptr<T> matrix;
};
Much simpler and less error prone. The unique pointer is now taking care of the resource management. Note that I allocate n * m objects of type T which means your matrix access operator will look like this:
T operator()(int i, int j)
{
return matrix.get()[i * n + j];
}
The unique_ptr can be moved but not copied which means you can now safely move Matrix2 but not copy. Copy semantics must be defined explicitly:
Matrix2(const Matrix2& other)
: n(other.n), m(other.m), matrix(new T[n * m])
{
std::copy(other.matrix.get(), other.matrix.get() + n * m, matrix.get());
}
Matrix2& operator=(const Matrix2& other)
{
if (this != &other)
{
n = other.n;
m = other.m;
matrix.reset(new T[n * m]);
std::copy(other.matrix.get(), other.matrix.get() + n * m, matrix.get());
}
return *this;
}
Note that this code is for demo purposes only and still lacks important aspects and should not be used in a real application.