Program fails when I call a method from constant reference - c++

So, I have a token class:
Token.h
class Token {
std::string name; // token name
int frequency;//frequency
Vector lines;//lines where the token is present
public:
//explanations for the methods in the Token.cpp
Token(std::string tokenname, int linenumber);
virtual ~Token();
const Vector getLines() const;
};
#endif /* TOKEN_H_ */
Token cpp
Token::Token(string tokenname, int linenumber) {
// TODO Auto-generated constructor stub
name = tokenname;
frequency=1;
lines.push_back(linenumber);
}
Token::~Token() {
// TODO Auto-generated destructor stub
}
std::string Token::getName() const{
return name;
}
int Token::getFrequency() const{
return frequency;
}
const Vector Token::getLines() const{
const Vector vec = lines;
return lines;
}
The program fails, when I pass it to the insert method of list class
class List {
private:
class Node {
public:
Token data;
Node* next;
Node(const Token &dataItem, Node* nextptr);
~Node();
};
Node* first;
int length;
public:
List();
virtual ~List();
void insert(const Token &t);
};
List.cpp:
List::Node::Node(const Token &dataItem, Node* nextptr): data(dataItem), next(nextptr){
}
List::Node::~Node(){
cout<<"dead"<<endl;
}
List::List() {
// TODO Auto-generated constructor stub
length = 0;
first = nullptr;
}
List::~List() {
// TODO Auto-generated destructor stub
Node* temp = first;
Node* newtmp;
while(temp->next != nullptr){
newtmp = temp->next;
delete temp;
temp = newtmp;
}
}
const int List::size(){
return length;
}
void List::insert (const Token &t){
Vector dammit = t.getLines();
}
I found out which line in the insert does it(Vector dammit = t.getLines()), so I leave it like that.
It gives me this error message:
double free or corruption (fasttop): 0x0000000000c34040 ***
And here something from main file if you want to run:
int main() {
// cout<<"tokens are here"<<endl;
//
Token hit("aca", 1);
Token hit2("ui", 2);
Token hit1("111", 3);
List list;
list.insert(hit);
list.insert(hit2);
list.insert(hit1);
}
Vector class:
class Vector {
int* store;
int capacity;
int next_index;
public:
Vector();
Vector(int initial_size);
Vector(const Vector &v);
virtual ~Vector();
void push_back(int item);
int pop_back();
const int size() const;
void resize();
void operator =(const Vector &v);
int& operator[] (int k);
const int& operator[] (int k) const;
friend std::ostream& operator<<(std::ostream& os, const Vector& v);
};
Vector::Vector() {
// TODO Auto-generated constructor stub
store = new int [1];
capacity = 1;
next_index = 0;
}
Vector::Vector(int initial_size){
store = new int [initial_size];
capacity = initial_size;
next_index = 0;
}
Vector::Vector(const Vector &v){
store = v.store;
capacity = v.capacity;
next_index = v.next_index;
}
Vector::~Vector() {
// TODO Auto-generated destructor stub
delete[] store;
}
void Vector::resize(){
std::cout<<"in resize"<<std::endl;
std::cout<<capacity<<std::endl;
int length = capacity;
capacity+=100;
int* tempArray;
tempArray = new int[capacity];
for (int i=0; i<length; i++){
tempArray[i] = store[i];
}
if (length>1)
delete[] store;
std::cout<<"finish re4size"<<std::endl;
store = tempArray;
}
void Vector::push_back(int item){
if(next_index >= capacity)
this->resize();
store[next_index] =item;
next_index++;
}
int Vector::pop_back(){
next_index = next_index-1;
int last = store[next_index];
return last;
}
void Vector::operator =(const Vector &v){
//delete[] store;
store = v.store;
capacity = v.capacity;
next_index = v.next_index;
}
const int Vector::size() const{
return next_index-1;
}
int& Vector::operator[] (int k){
//assert((k<next_index)&(k>=0));
return store[k];
}
const int& Vector::operator[] (int k) const{
//assert((k<next_index)&(k>=0));
return store[k];
}
ostream& operator<<(ostream& os, const Vector& v)
{
for(int i=0; i<=v.size(); i++){
os << v[i]<< ' ';
}
return os;
}

In
Vector::Vector(const Vector &v){
store = v.store;
capacity = v.capacity;
next_index = v.next_index;
}
You now have two vectors pointing to the same int* store;
In
void Vector::operator =(const Vector &v){
//delete[] store;
store = v.store;
capacity = v.capacity;
next_index = v.next_index;
}
You do the same thing.
when you call
const Vector Token::getLines() const{
const Vector vec = lines;
return lines;
}
vec = lines uses the copy constructor. You now have vec and lines pointing to the same store.
You return a copy of lines, this will trigger the copy constructor again. A third object now points to store.
When the stack unrolls, locally defined vec is destroyed. ~Vector deletes store. You now have two objects pointing to the same de-allocated store.
Kaboom! as soon as you try to do much of anything else with either of those Vectors. Looks like the destruction of the returned Vector hits first and causes the destructor to re-delete store.
You need to allocate storage for a new store and then copy the contents of source store into the new store in the = operator and the copy constructor.
Vector::Vector(const Vector &v){
capacity = v.capacity;
store=new int[capacity];
for (size_t index; index < capacity; index++)
{
store[index] = v.store[index];
}
next_index = v.next_index;
}
and
Vector & Vector::operator =(const Vector &v){
delete[] store;
capacity = v.capacity;
store=new int[capacity];
for (size_t index; index < capacity; index++)
{
store[index] = v.store[index];
}
next_index = v.next_index;
}
std::copy can be used in place of the for loop in C++11. Hoary old memcpy can also be used, but only because store is a primitive data type.
And while I'm editing, thanks Jarod42, one more little tweak:
const Vector & Token::getLines() const{ //note the return of a reference. This avoids
// making a copy of lines unless the caller really
// wants a copy.
// const Vector vec = lines; don't need to do this. lines is const-ified by the
// const on the return type of the function
return lines;
}

This error has nothing to do with calling a method with constant reference, but rather the function getLines(). For example, should you take hit1 and call the function getLines() directly, it will crash nonetheless. The issue is with how Token has a stack-allocated attribute Vector, which in turn has an int * attribute. This isn't necessarily an issue, but depending on how you implement those classes it can cause memory conflicts.
If you want to keep using your getLine() and can't use the <vector> libraries, you could change your Token's lines attribute to a Vector * and change all other syntax accordingly. Also remember to initialize your pointer lines memory or else it will crash.
However, I'd prefer to use as less dynamic-allocated memory as possible, if unnecessary. And like another user said, before while(temp->next != nullptr) you should have a condition if(temp != nullptr )

Related

C++ logical error with using dynamic arrays

Inside the insert function, I use dynamic memory allocation with the arrays ditems and tempItems.
Where I am using tempItems to be an array twice as big as ditems and also store all the items from ditems temporary; and then deleting and assigning ditems equaling tempItems
The code complies fine, but when testing it with say enough data requiring ditems to store 2000 elements, it appears that the ditems array is not getting any bigger.However if I was to set (arrayCap=2001;) in the constructor, then there is no problem.
I am new to using dynamic arrays and looking at other code using dynamic arrays, it doesn't look like I had made any mistakes. I can't use vectors for this task, so I am stuck with dynamic arrays, but I am not sure what is wrong here.
template <class T>
class Vector {
public:
typedef T* iterator;
Vector () {
arrayCap=1000;
ditems = new T[arrayCap];
}
T& operator[](unsigned int i) {
return ditems[i];
}
iterator begin () {
used=0;
return &ditems[used];
}
iterator end () {
return &ditems[used];
}
int size () { return used; }
void deletes(){
used--;
}
iterator insert (iterator position, const T& item) {
if(arrayCap-used<100)
{
temp=arrayCap;
arrayCap=2*arrayCap;
tempItems=new T[arrayCap];
for(int i=0; i<temp;i++)
{
tempItems[i]= ditems[i];
}
delete [] ditems;
ditems=tempItems;
}
for(Vector<T>::iterator i=&ditems[arrayCap-1]; i>position; i--)
{
*i=*(i-1);
}
used++;
*position= item;
return position;
}
private:
int arrayCap,temp;
T *ditems;
T *tempItems;
int used;
};
Moving the array to a new position invalidates the iterator position, it points to the old array. So i>position is undefined behavior.
You should calculate the index before moving the array and set position at the index in the new array.
template <class T>
class Vector {
public:
typedef T* iterator;
Vector () {
arrayCap=1000;
ditems = new T[arrayCap];
used = 0;
}
T& operator[](unsigned int i) {
return ditems[i];
}
iterator begin () {
return ditems;
}
iterator end () {
return &ditems[used];
}
int size () { return used; }
void deletes(){
used--;
}
iterator insert (iterator position, const T& item) {
if(arrayCap-used<100)
{
auto temp=arrayCap;
arrayCap*=2;
auto index = position - ditems;
auto tempItems=new T[arrayCap];
for(int i=0; i<temp;i++)
{
tempItems[i]= ditems[i];
}
delete [] ditems;
ditems = tempItems;
position = ditems + index;
}
for(Vector<T>::iterator i=&ditems[arrayCap-1]; i>position; i--)
{
*i=*(i-1);
}
used++;
*position = item;
return position;
}
private:
int arrayCap;
T *ditems;
int used;
};

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.

Segmentation fault when creating a row-major array

I'm trying to implement a row-major array, which is basically a single dimension representation of a 2D array.
This is my class definition
class RMA{
public:
RMA(){
size_=0;
row_=0;
column_=0;
arr_ = new double[size_];
}
RMA(int n, int m){
size_ = n*m;
column_ = m;
row_ = n;
if(size_== 0) arr_ = 0;
else arr_ = new double[size_];
}
RMA(const RMA& arr) {
size_ = arr.size_;
if(this != &arr){
delete [] arr_;
arr_ = new double[size_];
for(int i=0; i<size_; i++){
arr_[i] = arr.arr_[i];
}
}
return *this;
}
const double& operator() (int n, int m) const{
return arr_[n*column_+m];
}
double& operator()(int n, int m){
return arr_[n*column_+m];
}
~RMA(){delete[] arr_ ;}
private:
int size_;
int column_;
int row_;
double* arr_;
}
I've a calling function which creates the array.
RMA create_array() {
RMA arr;
arr = RMA(N, M);
std::cout<<"success";
return arr;
}
And this is my client
int main(int argc, char* argv[]) {
RMA arr = create_array();
return 0;
}
I end up getting segmentation fault. What am I doing wrong.
You use operations, that instead of cloning array, take a shallow copy of an object, and when destructors are used, they try to release the same memory block.
Implement the following operations:
RMA::RMA(const RMA&); // copy constructor - clone buffer
RMA& operator=(const &RMA); // assignment - clone buffer, release old
Also instead of:
RMA rma;
rma = RMA(a,b);
Use:
RMA rma = RMA(a,b) or RMA rma(a,b);
Edit: constructor code:
RMA::RMA(const RMA &rma) : size_(0), row_(0), column_(0), buffer_(0)
{
*this = rma;
}
RMA &operator=(const RMA&rma)
{
double *old = buffer_;
size_ = rma.size_;
row_ = rma.row_;
column_ = rma.column_;
buffer_ = new double[size_];
memcpy(buffer_, rma.buffer_, sizeof(buffer_[0]) * size_);
delete []old;
return *this;
}
The best solution is to get rid of all the new/delete, copy-constructors, and fluff. Use a private member variable to manage the memory, and follow the Rule of Zero. Like this:
struct RMA
{
RMA(size_t r = 0, size_t c = 0)
: row(r), column(c), arr(r * c) {}
const double& operator() (int n, int m) const
{ return arr[n * column + m]; }
double& operator() (int n, int m)
{ return arr[n * column + m]; }
private:
std::vector<double> arr;
size_t row, column;
};
That's it. You should not write any copy-constructor, assignment operator, move whatever, because the default-generated ones already do the right thing.
NB. row is actually redundant in my example too, you could remove it and calculate it when needed as arr.size() / column.
You could use .at( ) instead of [ ] on vector in order to throw an exception for out-of-bounds access, instead of causing undefined behaviour.

Issue in allocating memory for resizing stack

I have tried and tried to figure the mistake in my code , but I still can't find it.I have a Stack class Album, which i want to resize, and think i did it right.For some reason however most of the times the program crashes and maybe one in 10 works fine and I have no idea why.If you could point the mistake that would be great. So here is the code:
const Song Song::null_song;//static from Song class
class Album
{
Song* songs;
char* name;
int top;
int capacity;
bool full () const;
void resize ();
public:
...
}
And here are the functions, somewhere in them is the culprit.The problem happens when I try to push more items in Album then the predefined INIT_CAPACITY=4.I think it should work, but it doesn't, so the problem must be allocating the new memory.
const int INIT_CAPACITY=4;
std::ostream& operator<<(std::ostream& os, Album& p)
{
os<<"Name of Album:"<<p.name<<std::endl;
for(int i=0;i<=p.top;i++)
os<<p.songs[i]<<std::endl;
}
Album::Album(const char* p)
{
int len1=strlen(p);
name=new char [len1+1];
strcpy(name,p);
top=-1;
songs = new Song[INIT_CAPACITY];
capacity = INIT_CAPACITY;
}
Song Album::pop()
{
if (empty())
return Song::null_song;
return songs[top--];
}
Song Album::last() const
{
if (empty())
return Song::null_song;
return songs[top];
}
bool Album::push(Song x)
{
if (full())
resize();
songs[++top] = x;
return true;
}
void Album::resize()
{
capacity *= 2;
Song* newsongs = new Song[capacity];
for(int i = 0; i < capacity / 2; i++)
newsongs[i] = songs[i];
delete[] songs;
songs = newsongs;
}
bool Album::empty() const
{
return top == -1;
}
bool Album::full() const
{
return top == capacity-1;
}
Album::Album()
{
top=-1;
songs = new Song[INIT_CAPACITY];
capacity = INIT_CAPACITY;
name=new char [1];
name[0]='\0';
}
Album::~Album()
{
delete [] songs;
delete [] name;
}
Your Song also uses char* where it should use std::string.
It deletes this pointer in the destructor, but you haven't defined an assignment operator or copy constructor.
This makes all Songs contain invalid pointers once you have resized an Album.

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;
}