Why is my _capacity variable not updating? - c++

I'm supposed to create a header file for this one program that'll create a new array with an increased length. My professor said that there's nothing visibly wrong, but I keep getting an error saying that my _capacity variable isn't updating. What am I doing wrong?
I tried increasing _capacity by doing _capacity += _growth_factor and also created a new array T* new_stack = new T[_capacity + _growth_factor], so how come the array still has the old _capacity?
#include "ArrayStack.h"
template <typename T>
class ArrayStackConst : public ArrayStack<T>
{
public:
ArrayStackConst(size_t initial_size, size_t growth_factor)
: ArrayStack<T>(initial_size, growth_factor)
{
}
protected:
size_t _growth_factor;
T* _stack;
size_t _capacity;
size_t _top;
virtual void ensure_capacity() override
{
if (this->_top == this->_capacity) {
T* new_stack = new T[_capacity + _growth_factor];
for (int i = 0; i < _capacity; i++) {
new_stack[i] = _stack[i];
}
T* temp = _stack;
_stack = new_stack;
delete[] temp, new_stack;
_capacity += _growth_factor;
}
}
};

Related

How would I combine two ArrayLists that are of a special type?

The goal of this portion of the assignment is to create specialized implementations of an ArrayList of Addresses, or ArrayList<Address>. We are to combine two ArrayLists of this type, but our given starter code has me a bit lost on where to start.
First, we have a templated ArrayList from class in ArrayList.h, along with some functions to go with it:
template <class T>
class ArrayList {
public:
/**
* #brief Add copy of item to the end of the list, growing internal storage if needed
* #param insertItem Item to duplicate into the list
*/
void insertEnd(const T& insertItem);
/**
* YOU WILL IMPLEMENT IN AddressArrayList.h/cpp
*/
void combine(ArrayList<T>& otherList);
protected:
/**
* #brief Allocate new storage array double old capacity and copy
* existing items to it.
*/
void grow();
T* list; ///dynamic array holding stored items
int length; ///logical length of list - how many items are being stored
int maxSize; ///size of array used for storage
};
template <class T>
void ArrayList<T>::grow()
{
int newSize = maxSize * 2;
T* tempList = new T[newSize];
for(int i = 0; i < maxSize; i++)
tempList[i] = list[i];
maxSize = newSize;
delete [] list;
list = tempList;
}
template <class T>
void ArrayList<T>::insertEnd(const T& insertItem)
{
if(length == maxSize)
grow();
list[length] = insertItem;
length++;
}
After this, we have the definition of an Address in Address.h:
#ifndef ADDRESS_H
#define ADDRESS_H
#include <string>
#include <fstream>
struct Address {
std::string first;
std::string last;
std::string streetAddr;
std::string city;
std::string county;
std::string state;
int zipCode;
Address();
//Accepts comma seperated line of text with fields in order of member variables
explicit Address(const std::string& dataLine);
};
Finally, in AddressArrayList.cpp is the function we are supposed to implement. It is stated to be a "template specialization for an ArrayList of Addresses. This is defining a combine that will ONLY work for an ArrayList". My confusion begins at this point. The function should be implemented along the lines of something like listA.combine(listB), and given that fact and all the code given to us already, I think I need to use the this pointer, but what I've tried below resulted in failure, and I don't know where to go from here.
// #brief Move all items from otherList to the end of this List.
// #param otherList List to be combined into this one. It should end up empty.
template <>
void ArrayList<Address>::combine(ArrayList<Address>& otherList) {
grow();
for (int i = 0; i < this->length + otherList.length; i++) {
this->list[i + length] = otherList.list[i];
}
}
You can simply use 'insertEnd()' as NadavS suggested, but if the otherList is very long, then you may end up with calling grow() by insertEnd() multiple times, which is inefficient. To optimize this, I think you can modify grow() to increase the capacity only once.
template <class T>
void ArrayList<T>::grow(int more=0)
{
int newSize = maxSize + (more > 0 ? more : maxSize);
T* tempList = new T[newSize];
for(int i = 0; i < maxSize; i++)
tempList[i] = list[i];
maxSize = newSize;
delete [] list;
list = tempList;
}
template <>
void ArrayList<Address>::combine(ArrayList<Address>& otherList) {
grow(otherList.length);
for (int i = 0; i < this->length + otherList.length; i++) {
this->list[i + length] = otherList.list[i];
}
}

Custom container unnecessarily creating new elements instances when space is reserved

For my own education, I am trying to learn how to implement efficient custom containers in C++. I have now a basic working version of a my custom vector type. However, for some reason, when the vector has to be expanded to fit more elements (in which case a call to its inner 'reserve' function is made), it creates extra copies of elements.
To help explaining what I mean, I show below a minimum reproducible example. Let a minimum version of CustomVector class look like the following:
template<class T>
class CustomVector
{
private:
size_t m_size = 0;
size_t m_capacity = 1;
T *m_data = nullptr;
public:
CustomVector()
{
}
CustomVector(const size_t new_capacity)
{
m_capacity = new_capacity;
m_size = 0;
m_data = new T[m_capacity]();
}
~CustomVector()
{
if (m_data != nullptr)
delete[] m_data;
}
void reserve(size_t new_capacity)
{
if (m_data == nullptr)
{
m_capacity = new_capacity;
m_size = 0;
m_data = new T[m_capacity]();
}
else if (new_capacity > m_capacity)
{
T* new_data = new T[new_capacity]();
memmove(new_data, m_data, (m_size) * sizeof(T));
delete[] m_data;
m_capacity = new_capacity;
m_data = new_data;
}
}
void push_back(const T & value)
{
if (m_data == nullptr)
{
m_capacity = 1;
m_size = 0;
m_data = new T[m_capacity]();
m_data[0] = value;
}
else if (m_size + 1 >= m_capacity)
{
reserve(m_capacity*2);
}
else
{
m_data[m_size-1] = value;
m_size++;
}
}
};
Now, to facilitate seeing the problem, I also create a class called Object. Each new instance of such class that is created automatically receives an unique id number:
class Object
{
private:
static int idCounter;
public:
int id;
Object()
{
id = idCounter;
idCounter++;
}
};
int Object::idCounter = 0;
Lastly, here is how the main function of this example looks like:
int main()
{
CustomVector<Object> objects; //comment this line...
//std::vector<Object> objects; //...and uncomment this to try with std::vector
Object x;
printf("%d\n", x.id);
objects.push_back(x);
Object y;
printf("%d\n", y.id);
objects.push_back(y);
Object z;
printf("%d ", z.id);
system("Pause");
return 0;
}
The output, using my CustomVector as the container, is:
0 2 5
While the output using a std::vector as the container is:
0 1 2
The desirable behavior for me is exactly that of std::vector, that is, pushing back instances of classes should create full new temporary instances of such class.
Could someone help me understand what am I doing wrong?
The problem is most likely this line in the push_back function:
m_data = new T[m_capacity]();
This will cause the creation of m_capacity number of T objects, and therefore m_capacity calls to the T constructor. This is bad if the T constructor is expensive (not to mention some beginners do input and other things in the constructor).
What std::vector most likely does is keeping a buffer of bytes, and then when pushing back it does placement new to construct an object in place in some position in the buffer.

Resize method intArray

I'm writting an IntArray class for college but don't know how to write my resize method efficiently. What I have doesn't support resizing to smaller lists and I don't know how to fix that..
Here is my code:
void IntArray::resize(unsigned int size){
for (int i = size;i<length;i++){
data[i] = 0;
}
length = size;
}
header file
#ifndef INTARRAY_H_
#define INTARRAY_H_
#include <iostream>
using namespace std;
class IntArray{
private:
int length;
int * data;
public:
IntArray(int size = 0);
IntArray(const IntArray& other);
IntArray& operator=(const IntArray& original);
int getSize() const { return length; };
int& operator[](unsigned int i);
void resize(unsigned int size);
void insertBefore(int value, int index);
friend ostream& operator<<(ostream& out, const IntArray& list);
~IntArray(){ delete[] data; };
};
When you need to resize an you are actually going to create a new array, copy the old into the new and then delete the old array.
void IntArray::resize(unsigned int size){
if (size <= length) // if we are making it smaller reset the size and do nothnig
{
my_size = size
return;
}
int * temparr = new int[size];
// copy data
for (unsigned int i = 0; i < length; ++i)
temparr[i] = data[i];
delete [] data; // get rid of the old array
data = temparr; // set data to the new array
length = size; // set the new size
}
You should also have a capacity member that tracks the actual size of the array like a std::vector. That way you can have an array that is bigger than what you need to as it grows there would need to be less re -llocations.

Declaring the templates in c++

// stdafx.h
// stdafx.h : include file for standard system include files,
// or project specific include files that are used frequently, but
// are changed infrequently
//
#include "targetver.h"
#include <stdio.h>
#include <tchar.h>
#include <iostream>
using namespace std;
#include "Animal.h"
// TODO: reference additional headers your program requires here
class Animal
{
private:
int itsWeight;
public:
Animal(int);
Animal();
~Animal() {}
int getWeight() const { return itsWeight; }
void Display() const;
};
template <class T>
class Array
{
private:
T *pType;
int itsSize;
const int defaultSize = 10;
public:
//constructors
Array(int itsSize = defaultSize);
Array(const Array &rhs);
~Array() { delete[] pType; }
//operators
Array& operator=(const Array&);
T& operator[](int offSet){ return pType[offSet]; }
const T& operator[](int offSet) const { return pType[offSet]; }
//methods of Access
int getSize() const { return itsSize; }
};
//constructor
template <class T>
Array<T>::Array(int size) :
itsSize(size)
{
pType = new T[size];
for (int i = 0; i < size; i++)
{
pType[i] = 0;
}
}
//copy-constructor
template <class T>
Array<T>::Array(const Array &rhs)
{
itsSize = rhs.getSize();
pType = new T[itsSize];
for (int i = 0; i < itsSize; i++)
{
pType[i] = rhs[i];
}
}
//operator prisvoeniya
template <class T>
Array<T>& Array<T>::operator=(const Array &rhs)
{
if (this == &rhs)
return *this;
delete[] pType;
itsSize = rhs.getSize();
pType = new T[itsSize];
for (int i = 0; i < itsSize; i++)
{
pType[i] = rhs[i];
}
return *this;
}
//this is the file "Animal.cpp"
#include "stdafx.h"
#include "Animal.h"
Animal::Animal()
{
itsWeight = 0;
}
Animal::Animal(int weight)
{
itsWeight = weight;
}
void Animal::Display() const
{
cout << itsWeight;
}
// the main function
#include "stdafx.h"
int_tmain(int argc, _TCHAR* argv[])
{
Array<int> theArray; //Integer array
Array<Animal> theZoo; //Animal array
Animal *pAnimal;
//filling the array
for (int i = 0; i < theArray.getSize(); i++)
{
theArray[i] = i * 2;
pAnimal = new Animal[i * 3];
theZoo[i] = *pAnimal;
delete pAnimal;
}
for (int j = 0; j < theArray.getSize(); j++)
{
cout << "theArray[" << j << "]:\t";
cout << theArray[j]<<"\t\t";
cout << "theZoo[" << j << "]:\t";
theZoo[j].Display();
cout << endl;
}
return 0;
}
The problem is that: The compiler gives me the errors
Error 1 error C2648: 'Array<int>::defaultSize' : use of member as default parameter requires static member
d:\documents\work\c++ files\tigrans\homework10\templates\templates\templates\animal.h 28 1 Templates
Error 2 error C2648: 'Array<Animal>::defaultSize' : use of member as default parameter requires static member
d:\documents\work\c++ files\tigrans\homework10\templates\templates\templates\animal.h 28 1 Templates
Anybody can help me to understand that. I change the
const int defaultSize=10;
to
static const int defaultSize=10
then there is not errors but in that time show Debug Assertion Failed!
This part of your code is dodgy
{
pAnimal = new Animal[i * 3];
theZoo[i] = *pAnimal;
delete pAnimal;
}
The first line allocates an array of i*3 Animals, using their default constructor (which makes an Animal with itsWeight=0). In the second line you assign the first these newly allocated Animals to theZoo[i]. Finally, the third line tries to de-allocate the Animals.
The last line contains an error, since you call delete on a pointer obtained with new [].
The whole concept of creating objects on the heap only to immediately destroy them is quite dubious -- perhaps you come from another programming language, where this is the only way to create things? First, you could simply use an automatic variable
{
Animal a; // or a(i*3);
theZoo[i] = a;
}
or yet briefer
{
theZoo[i] = Animal(i*3);
}
(Note the if you would use a std container, you could say theZoo.emplace_back(i*3);, avoiding the copy of Animal.)

how to create a contiguous 2d array in c++?

I want to create a function that returns a contiguous 2D array in C++.
It is not a problem to create the array using the command:
int (*v)[cols] = new (int[rows][cols]);
However, I am not sure how to return this array as a general type for a function. The function is:
NOT_SURE_WHAT_TYPE create_array(int rows, int cols)
{
int (*v)[cols] = new (int[rows][cols]);
return v;
}
I tried double*[] and double** and both don't work. I wouldn't want to use double*, since I want to access this array from outside as a 2D array.
Related question: How do I declare a 2d array in C++ using new?
If you want to create an array where the data is contiguous and you don't want a 1-dimensional array (i.e. you want to use the [][] syntax), then the following should work. It creates an array of pointers, and each pointer points to a position into a pool of memory.
#include <iostream>
#include <exception>
template <typename T>
T** create2DArray(unsigned nrows, unsigned ncols, const T& val = T())
{
if (nrows == 0)
throw std::invalid_argument("number of rows is 0");
if (ncols == 0)
throw std::invalid_argument("number of columns is 0");
T** ptr = nullptr;
T* pool = nullptr;
try
{
ptr = new T*[nrows]; // allocate pointers (can throw here)
pool = new T[nrows*ncols]{val}; // allocate pool (can throw here)
// now point the row pointers to the appropriate positions in
// the memory pool
for (unsigned i = 0; i < nrows; ++i, pool += ncols )
ptr[i] = pool;
// Done.
return ptr;
}
catch (std::bad_alloc& ex)
{
delete [] ptr; // either this is nullptr or it was allocated
throw ex; // memory allocation error
}
}
template <typename T>
void delete2DArray(T** arr)
{
delete [] arr[0]; // remove the pool
delete [] arr; // remove the pointers
}
int main()
{
try
{
double **dPtr = create2DArray<double>(10,10);
dPtr[0][0] = 10; // for example
delete2DArray(dPtr); // free the memory
}
catch(std::bad_alloc& ex)
{
std::cout << "Could not allocate array";
}
}
Note that only 2 allocations are done. Not only is this more efficient due to the lesser amounts of allocations done, we now have a better chance of doing a rollback of the allocated memory if a memory allocation fails, unlike the "traditional" way of allocating a 2D array in non-contiguous memory:
// The "traditional" non-contiguous allocation of a 2D array (assume N x M)
T** ptr;
ptr = new T*[N];
for (int i = 0; i < N; ++i)
ptr[i] = new T [M]; // <<-- What happens if new[] throws at some iteration?
If new[] throws an exception somewhere during the operation of the for loop, you have to roll back all of the successful calls to new[] that happened previously -- that requires more code and adds complexity.
Note how you deallocate the memory in the contiguous version -- just two calls to delete[] when allocated contiguously instead of a loop calling delete[] for each row.
Also, since the data is in contiguous memory, algorithms, functions, etc. that assume that the data is in contiguous memory, just like a one-dimensional array, can now be used by specifying the start and end range for the M*N matrix:
[&array[0][0], &array[M-1][N])
For example:
std::sort(&myArray[0][0], &myArray[M-1][N]);
will sort the entire matrix in ascending order, starting from index [0][0] up until the last index [M-1][N-1].
You can improve on the design by making this a true class instead of having allocation / deallocation as 2 separate functions.
Edit: The class is not RAII-like, just as the comment says. I leave that as an exercise for the reader. One thing missing from the code above is the check that nRows and nCols are > 0 when creating such an array.
Edit 2: Added a try-catch to ensure a proper roll back of the memory allocation is done if a std::bad_alloc exception is thrown attempting to allocate memory.
Edit: For a 3 dimensional array example of code similar to the above see this answer. Included is code to roll back allocations if the allocation fails.
Edit: Rudimentary RAII class added:
template <typename T>
class Array2D
{
T** data_ptr;
unsigned m_rows;
unsigned m_cols;
T** create2DArray(unsigned nrows, unsigned ncols, const T& val = T())
{
T** ptr = nullptr;
T* pool = nullptr;
try
{
ptr = new T*[nrows]; // allocate pointers (can throw here)
pool = new T[nrows*ncols]{ val }; // allocate pool (can throw here)
// now point the row pointers to the appropriate positions in
// the memory pool
for (unsigned i = 0; i < nrows; ++i, pool += ncols)
ptr[i] = pool;
// Done.
return ptr;
}
catch (std::bad_alloc& ex)
{
delete[] ptr; // either this is nullptr or it was allocated
throw ex; // memory allocation error
}
}
public:
typedef T value_type;
T** data() {
return data_ptr;
}
unsigned get_rows() const {
return m_rows;
}
unsigned get_cols() const {
return m_cols;
}
Array2D() : data_ptr(nullptr), m_rows(0), m_cols(0) {}
Array2D(unsigned rows, unsigned cols, const T& val = T())
{
if (rows == 0)
throw std::invalid_argument("number of rows is 0");
if (cols == 0)
throw std::invalid_argument("number of columns is 0");
data_ptr = create2DArray(rows, cols, val);
m_rows = rows;
m_cols = cols;
}
~Array2D()
{
if (data_ptr)
{
delete[] data_ptr[0]; // remove the pool
delete[] data_ptr; // remove the pointers
}
}
Array2D(const Array2D& rhs) : m_rows(rhs.m_rows), m_cols(rhs.m_cols)
{
data_ptr = create2DArray(m_rows, m_cols);
std::copy(&rhs.data_ptr[0][0], &rhs.data_ptr[m_rows-1][m_cols], &data_ptr[0][0]);
}
Array2D(Array2D&& rhs) noexcept
{
data_ptr = rhs.data_ptr;
m_rows = rhs.m_rows;
m_cols = rhs.m_cols;
rhs.data_ptr = nullptr;
}
Array2D& operator=(Array2D&& rhs) noexcept
{
if (&rhs != this)
{
swap(rhs, *this);
rhs.data_ptr = nullptr;
}
return *this;
}
void swap(Array2D& left, Array2D& right)
{
std::swap(left.data_ptr, right.data_ptr);
std::swap(left.m_cols, right.m_cols);
std::swap(left.m_rows, right.m_rows);
}
Array2D& operator = (const Array2D& rhs)
{
if (&rhs != this)
{
Array2D temp(rhs);
swap(*this, temp);
}
return *this;
}
T* operator[](unsigned row)
{
return data_ptr[row];
}
const T* operator[](unsigned row) const
{
return data_ptr[row];
}
void create(unsigned rows, unsigned cols, const T& val = T())
{
*this = Array2D(rows, cols, val);
}
};
int main()
{
try
{
Array2D<double> dPtr(10, 10);
std::cout << dPtr[0][0] << " " << dPtr[1][1] << "\n";
}
catch (std::exception& ex)
{
std::cout << ex.what();
}
}
Unless the size of the two dimensions is known at compile time, your don't have much choice: allocate a single rows*cols array of ints, and roll your own 2D indexing with integer multiplication and addition. Wrapping this in a class can produce a nice-looking syntax for accessing array elements with square bracket operator. Since your array is 2D, you will need to use proxy (AKA "surrogate") objects for the first level of data access.
Here is a small sample code that uses std::vector<T> for maintaining a contiguous memory region in dynamic memory:
template<class T>
class Array2D {
vector<T> data;
size_t cols;
public:
// This is the surrogate object for the second-level indexing
template <class U>
class Array2DIndexer {
size_t offset;
vector<U> &data;
public:
Array2DIndexer(size_t o, vector<U> &dt) : offset(o), data(dt) {}
// Second-level indexing is done in this function
T& operator[](size_t index) {
return data[offset+index];
}
};
Array2D(size_t r, size_t c) : data (r*c), cols(c) {}
// First-level indexing is done in this function.
Array2DIndexer<T> operator[](size_t index) {
return Array2DIndexer<T>(index*cols, data);
}
};
You can now use Array2D<int> as if it were a built-in C++ array:
Array2D<int> a2d(10, 20);
for (int r = 0 ; r != 10 ; r++) {
for (int c = 0 ; c != 20 ; c++) {
a2d[r][c] = r+2*c+1;
}
}
Running demo on ideone.
Since you're using C++ and not C, I would recommend to use one vector instead of messing around with new/delete.
You can define one contiguous block of memory like this:
std::vector<int> my_matrix(rows*cols);
And now you access this vector in a 2d-array-like way with the formula i*n + j, with i being the row index, j the column index and n the length of a row:
my_matrix[i*n + j];
That's the same as accessing a 2d array with array[i][j]. But now you have the advantage of one contiguous block of memory, you don't need to bother about new/delete and you can easily share and return this vector object with functions.
handling raw memory ressources is often icky. Best shot is a simple wrapper as :
struct array2D : private std::vector<int>
{
typedef std::vector<int> base_type;
array2D() : base_type(), height_(0), width_(0) {}
array2D(std::size_t h, std::size_t w) : base_type(h*w), height_(h), width_(w);
int operator()(std::size_t i, std::size_t j) const
{
return base_type::operator[](i+j*height_);
}
int& operator()(std::size_t i, std::size_t j)
{
return base_type::operator[](i+j*height_);
}
std::size_t rows() const { return height_; }
std::size_t cols() const { return width_; }
private:
std::size_t height_, width_;
}
private inheritance let you grab all the goodies from vector, just add your 2D constructor. Ressources management is free as vector ctor/dtor will do their magic. Obviously, the i+h*j can be changed to whateever storage order you want.
vector< vector< int > > is 2D but won't be contiguous in memory.
Your function then become :
array2D create_array(int rows, int cols)
{
return array2D(cols,rows);
}
EDIT:
You can also retrieve other vector interface parts like begin/end or size with the usign clause to make the private inherited member functions public again.
None of the ways of defining a 2D dynamic array in standard C++ are entirely satisfactory in my opinion.
You end up having to roll your own solutions. Luckily there is already a solution in Boost. boost::multi_array:
#include "boost/multi_array.hpp"
template<typename T>
boost::multi_array<T, 2> create_array(int rows, int cols) {
auto dims = boost::extents[rows][cols];
return boost::multi_array<T, 2>(dims);
}
int main() {
auto array = create_array<int>(4, 3);
array[3][2] = 0;
}
Live demo.
The "Rudimentary RAll" class provided by PaulMcKenzie is an excellent solution. In my use of it I did find a memory leak which is fixed in the version shown below.
The memory leak was due to an issue with
Array2D& operator=(Array2D&& rhs) noexcept.
The statement rhs.m_dataPtr = nullPtr needed to be removed in order to allow the rhs destructor to delete the original data (pool and pointers) swapped from lhs.
Here is the corrected code for the "Rudimentary RAll" class provided by PaulMcKenzie
template <typename T>
class Array2D
{
T** data_ptr;
unsigned m_rows;
unsigned m_cols;
T** create2DArray(unsigned nrows, unsigned ncols, const T& val = T())
{
T** ptr = nullptr;
T* pool = nullptr;
try
{
ptr = new T*[nrows]; // allocate pointers (can throw here)
pool = new T[nrows*ncols]{ val }; // allocate pool (can throw here)
// now point the row pointers to the appropriate positions in
// the memory pool
for (unsigned i = 0; i < nrows; ++i, pool += ncols)
ptr[i] = pool;
// Done.
return ptr;
}
catch (std::bad_alloc& ex)
{
delete[] ptr; // either this is nullptr or it was allocated
throw ex; // memory allocation error
}
}
public:
typedef T value_type;
T** data() {
return data_ptr;
}
unsigned get_rows() const {
return m_rows;
}
unsigned get_cols() const {
return m_cols;
}
Array2D() : data_ptr(nullptr), m_rows(0), m_cols(0) {}
Array2D(unsigned rows, unsigned cols, const T& val = T())
{
if (rows == 0)
throw std::invalid_argument("number of rows is 0");
if (cols == 0)
throw std::invalid_argument("number of columns is 0");
data_ptr = create2DArray(rows, cols, val);
m_rows = rows;
m_cols = cols;
}
~Array2D()
{
if (data_ptr)
{
delete[] data_ptr[0]; // remove the pool
delete[] data_ptr; // remove the pointers
}
}
Array2D(const Array2D& rhs) : m_rows(rhs.m_rows), m_cols(rhs.m_cols)
{
data_ptr = create2DArray(m_rows, m_cols);
std::copy(&rhs.data_ptr[0][0], &rhs.data_ptr[m_rows-1][m_cols], &data_ptr[0][0]);
}
Array2D(Array2D&& rhs) noexcept
{
data_ptr = rhs.data_ptr;
m_rows = rhs.m_rows;
m_cols = rhs.m_cols;
rhs.data_ptr = nullptr;
}
Array2D& operator=(Array2D&& rhs) noexcept
{
if (&rhs != this)
{
swap(rhs, *this);
}
return *this;
}
void swap(Array2D& left, Array2D& right)
{
std::swap(left.data_ptr, right.data_ptr);
std::swap(left.m_cols, right.m_cols);
std::swap(left.m_rows, right.m_rows);
}
Array2D& operator = (const Array2D& rhs)
{
if (&rhs != this)
{
Array2D temp(rhs);
swap(*this, temp);
}
return *this;
}
T* operator[](unsigned row)
{
return data_ptr[row];
}
const T* operator[](unsigned row) const
{
return data_ptr[row];
}
void create(unsigned rows, unsigned cols, const T& val = T())
{
*this = Array2D(rows, cols, val);
}
};
int main()
{
try
{
Array2D<double> dPtr(10, 10);
std::cout << dPtr[0][0] << " " << a2[0][0] << "\n";
}
catch (std::exception& ex)
{
std::cout << ex.what();
}
}
I think you should write a simple class to wrap a 1-dim array. Then you can implement a 2-dim array with operator() overloading for getting values and deconstruct func for release the memory. Code as below:
#include <assert.h>
template <typename T>
class Array_2D
{
private:
T *data_inside;
public:
int size[2];
Array_2D(int row, int column);
~Array_2D();
//
T operator()(int index1, int index2){
return data_inside[get_index(index1, index2)];
}
int get_index(int index1, int index2){
if(index1>=0 and index1<size[0] and index2>=0 and index2<=size[1]){
return index1*size[0] + index2;
}else{
assert("wrong index for array!" == "True");
}
}
};
template <typename T>
Array_2D<T>::Array_2D(int row, int column)
{
size[0] = row;
size[1] = column;
data_inside = new T[row*column];
}
template <typename T>
Array_2D<T>::~Array_2D()
{
// 使用析构函数,自动释放资源
delete[] data_inside;
}