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;
}
Related
I've started writing some code for a List class and I'm lost. I need it for my Arduino project – I can't use STL.
Here's the code.
#include <iostream>
template <typename T>
class List
{
public:
List<typename T>()
{
m_Count = 0;
m_Data = nullptr;
}
~List()
{
free(m_Data);
}
void Push(const T& element)
{
m_Count++;
T* alloc = (T*)malloc(size_t(sizeof(T) * m_Count));
memcpy(alloc, m_Data, m_Count * sizeof(T));
*(m_Data + sizeof(T) * (m_Count - 1)) = element;
}
T* operator [](unsigned int x) const
{
return (m_Data + x * sizeof(T));
}
private:
T* m_Data;
uint64_t m_Count;
};
struct Vertex
{
int x;
int y;
};
int main()
{
List<Vertex> list;
list.Push({ 0, 1 });
list.Push({ 2, 3 });
list.Push({ 4, 5 });
std::cout << list[0]->x << list[1]->x << list[2]->x;
}
The problem lies somewhere in the Push method: When I call memcpy, the program triggers a compiler breakpoint.
The essential problem is in the line *(m_Data + sizeof(T) * (m_Count - 1)) = element;. Here, you are attempting to copy the given element into the old (i.e. pre-existing) m_Data array; this will be nullptr the first time the Push function is called (and one element too small every other time)†.
So, you need to first release the old data (with free(m_Data)), then assign your newly-allocated memory to that pointer (m_Data = alloc), and only then copy element to that array's last element.
However, as others have said, why are you using malloc and free in C++? The code below replaces those calls with new[] and delete[] (although using a std::vector would likely be easier/better/safer, if that were possible).
#include <iostream>
template <typename T>
class List {
public:
List<T>() { // Don't need "typename" here!
m_Count = 0;
m_Data = nullptr;
}
~List() {
delete[] m_Data;
}
void Push(const T& element) {
m_Count++;
T* alloc = new T[m_Count];
for (uint64_t i = 0; i < m_Count - 1; ++i) alloc[i] = m_Data[i]; // Copy old data (if any)
delete[] m_Data; // Release old data
m_Data = alloc; // Assign newly-allocated memory to m_Data
m_Data[m_Count - 1] = element; // Why use pointer arithmetic when you have the [] operator?
}
T* operator [](unsigned int x) const {
return &m_Data[x];
}
private:
T* m_Data;
uint64_t m_Count;
};
struct Vertex {
int x;
int y;
};
int main()
{
List<Vertex> list;
list.Push({ 0, 1 });
list.Push({ 2, 3 });
list.Push({ 4, 5 });
std::cout << list[0]->x << list[1]->x << list[2]->x << std::endl;
std::cout << list[0]->y << list[1]->y << list[2]->y << std::endl;
return 0;
}
I have made a couple of other 'minor improvements' to your code (your operator [] looked very suspicious, as the size of the pointed-to object is inherently taken into account when doing pointer arithmetic); there are others that could be made but that would, IMHO, deviate too far from the code you posted.
† Actually, it will always be nullptr in your code, as you never assign anything else to it.
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.
I came across a exercise on the web, this is the text:
Write a class int_stack that will manage a stack of integers. The
integers values will be stored in a dynamically allocated array.
This class will propose the following member functions :
int_stack (int n) constructor that will dynamically allocate n
integers,
int_stack ( ) constructor allocating 20 integers,
~ int_stack ( ) destructor,
int empty ( ) the return value is 1 if the stack is empty, 0
otherwise,
int full ( ) the return value is 1 if the stack is full, 0 otherwise,
void operator < (int p) pushes (add) the p value on the stack,
int operator >(int p) returns (and remove) the value on the top of
the stack
I've tried to implement it, but the > (pull) operator won't work.
Here's my code:
int_stack.h
class int_stack
{
private:
int* stack;
unsigned int n, p;
void init(unsigned int n);
public:
int_stack(unsigned int n);
int_stack();
~int_stack();
int empty();
int full();
void operator <(int i);
int operator >(int i);
};
int_stack.cpp
#include "int_stack.h"
void int_stack::init(unsigned int n)
{
this->stack = new int[n];
this->p = 0;
}
int_stack::int_stack(unsigned int n)
{
this->init(n);
}
int_stack::int_stack()
{
this->init(20);
}
int_stack::~int_stack()
{
delete this->stack;
}
int int_stack::empty()
{
return (this->p == 0 ? 1 : 0);
}
int int_stack::full()
{
return (this->p == n-1 ? 1 : 0);
}
void int_stack::operator <(int i)
{
if (!this->full())
this->stack[p++] = i;
}
int int_stack::operator >(int i)
{
if(!this->empty())
return this->stack[p--];
return 0;
}
What am I doing wrong?
In addition to getting the indexing right, the class needs a copy constructor and an assignment operator. As written you'll get multiple deletes of the same data block:
int_stack s0;
int_stack s1(s0); // uh-oh
Both destructors will delete the array allocated by the constructor for s0.
There are several major flaws with you code:
Unless you want to resize the stack every time you push or pop something onto or off of it, respectively, you probably want to use a linked-list- or deque- style storage structure instead of a vector/array-style.
Overloading operator< and operator> to do what amounts to extraction and insertion is a terrible interface choice. I would urge against using operators for those operations:
void int_stack::push(int i)
{
// push an element onto the stack
}
int int_stack::pop()
{
// pop an element off of the stack
}
Because you are not implementing it as a linked-list or deque, when you go to push elements, you can (and eventually will) attempt to write outside the bounds of the memory you allocated.
Finally, you do not delete your stack properly. If you use new [], you must also use delete [].
The choice of interface is quite bad, but ignoring that fact consider what your members mean, in particular p. The index p refers to the location above the last added element. When you return the value in the pop operation you are reading the value from that location, but that location does not have a value:
int int_stack::operator >(int i)
{
if(!this->empty())
return this->stack[p--]; // <-- predecrement!
return 0;
}
Regarding the interface, operator< and operator> are unnatural choices for the push and pop operations. When someone reads in code s < 5 they interpret that you are comparing s with 5, not inserting an element into the stack s. That is going to be the source of confusion.
Worse than operator< is operator> defined as int operator>(int). User code to read a value will end up looking as:
value = s > 5;
That looks like comparing s to 5, and storing the result into value. Moreover, the actual behavior is completely independent on the argument 5, the same operation can be spelled as s > -1 or even s > 5.3
Here is the working implementation I came up with.
It implements a copy constructor and the assignment operator.
Also, the indexing works, and the interface has changed from the < and > operators to two simple push(int) and int pop() functions.
It throws exceptions when you try to push/pop over the boundaries.
int_stack.h
#include <exception>
class int_stack
{
private:
int* stack;
unsigned int n, p;
void init(unsigned int n);
void copy(int_stack& other);
public:
int_stack(unsigned int n);
int_stack();
int_stack(int_stack& other);
int_stack& operator=(int_stack& other);
~int_stack();
int empty();
int full();
void push(int i);
int pop();
class OutOfBoundariesException: public std::exception {};
};
int_stack.cpp
#include "int_stack.h"
void int_stack::init(unsigned int _n)
{
n = _n;
stack = new int[n];
p = 0;
}
int_stack::int_stack(unsigned int n)
{
init(n);
}
int_stack::int_stack()
{
init(20);
}
int_stack::int_stack(int_stack& other)
{
copy(other);
}
int_stack& int_stack::operator=(int_stack& other)
{
copy(other);
return *this;
}
void int_stack::copy(int_stack& other)
{
n = other.n;
p = other.p;
stack = new int[n];
for (unsigned int i = 0; i < n; i++)
stack[i] = other.stack[i];
}
int_stack::~int_stack()
{
delete[] stack;
}
int int_stack::empty()
{
return (p == 0 ? 1 : 0);
}
int int_stack::full()
{
return (p == n ? 1 : 0);
}
void int_stack::push(int i)
{
if (!full())
stack[(++p)-1] = i;
else
throw new OutOfBoundariesException;
}
int int_stack::pop()
{
if (!empty())
return stack[(p--)-1];
else
throw new OutOfBoundariesException;
return 0;
}
I've created custom class, that shall behave like a matrix. I've got there some basic operations and everything seems to be working well... Nevertheless, I can't find out, what shall I write to destructor of this class to free all allocated memory. Would you please advice me?
class CMatrix {
public:
class Proxy {
friend class CMatrix;
const CMatrix* cmat;
CMatrix *matrix;
size_t n;
Proxy(const CMatrix& m, size_t i)
: cmat(&m), matrix(), n(i) {
}
Proxy(CMatrix& m, size_t i)
: cmat(&m), matrix(&m), n(i) {
}
public:
const double& operator[](size_t j) const {
return cmat->_arrayofarrays[n * cmat->y + j];
}
double& operator[](size_t j) {
if (matrix) {
return matrix->_arrayofarrays[n * cmat->y + j];
} else return cmat->_arrayofarrays[cmat->y];
}
};
const Proxy operator[](size_t i) const {
return Proxy(*this, i);
}
Proxy operator[](size_t i) {
return Proxy(*this, i);
}
CMatrix() {
_arrayofarrays = NULL;
x = 0;
y = 0;
};
// constructor
CMatrix(size_t x, size_t y) : _arrayofarrays(), x(x), y(y) {
_arrayofarrays = new double[ x * y ]();
}
// destructor
~CMatrix() {
// ?!?!???!?!?!?!!!!?!?!?
// #$#%#^$!!!!#$#%!!
}
// copy constructor
CMatrix(const CMatrix& other) : _arrayofarrays(), x(other.x), y(other.y) {
delete [] _arrayofarrays;
_arrayofarrays = new double[x * y];
if (_arrayofarrays)
std::copy(other._arrayofarrays, other._arrayofarrays + (x * y), _arrayofarrays);
}
CMatrix& operator =(const CMatrix& rval) {
delete [] _arrayofarrays;
_arrayofarrays = new double[ rval.x * rval.y];
std::copy(rval._arrayofarrays, rval._arrayofarrays + (rval.x * rval.y), _arrayofarrays);
x = rval.x;
y = rval.y;
return *this;
}
double *_arrayofarrays;
size_t x;
size_t y;
};
EDIT:
actually now I've realized, that it crashes after running this part of code. Before calling this code, I have there a instances of my class, let's call it a,b,c and then I want to set a = b-c;
this works well for the first time...but when I want to repeat it, then it crashes
CMatrix CMatrix::operator-(const CMatrix &matrix) const {
if (this->x != matrix.x || this->y != matrix.y) {
throw CSizeException(matrix.y, matrix.x, this->y, this->x, '+');
};
CMatrix m(this->x, this->y);
CMatrix l(matrix.x, matrix.y);
l._arrayofarrays = this->_arrayofarrays;
CMatrix o(matrix.y, matrix.y);
o = matrix;
CMatrix result(this->x, this->y);
for (unsigned int i = 0; i < this->x; i++)
for (unsigned int j = 0; j < this->y; j++)
m[i][j] = l[i][j] - o[i][j];
return m;
}
Something like this?
~CMatrix() {
delete[] _arrayofarrays;
}
All arrays allocated with new[] must be destroyed by a matching call to delete[]. Moreover, you can remove the delete[] statement from the copy constructor:
CMatrix(const CMatrix& other) : _arrayofarrays(), x(other.x), y(other.y) {
// delete [] _arrayofarrays;
// ^^^^^^^^^^^^^^^^^^^^^^^^^
// This is unnecessary: you are creating a new object, so this data member
// is not pointing to any previously allocated array
// ...
}
UPDATE:
(from the comments) I can't use this code... _arrayofarrays is actually 2D array, so this causes that running of the program fails..probably segfault
This is incorrect: _arrayofarrays is a 1D array, and calling delete[] is the proper way do destroy it. If doing so leads to a segfault, probably you're doing something wrong in the rest of your code.
As an advice, it is usually a good idea to avoid manual memory management through raw pointers, new, and delete (or their array counterparts), because it is error-prone and easily leads to memory leaks or dereferencing of invalid pointers/reference.
Consider using standard containers such as std::vector<> or std::deque<> instead.
EDIT:
In the code of operator - you are doing:
l._arrayofarrays = this->_arrayofarrays;
This way you are having two matrix objects which encapsulate the same array: therefore, deleting one of the two will make the other one invalid as well. This is probably the root cause of your problem.
Also, you are creating way too many temporaries in there. Unless I'm missing something, this should be enough:
CMatrix CMatrix::operator-(const CMatrix &matrix) const {
if (this->x != matrix.x || this->y != matrix.y) {
throw CSizeException(matrix.y, matrix.x, this->y, this->x, '+');
};
CMatrix result(this->x, this->y);
for (unsigned int i = 0; i < this->x; i++)
for (unsigned int j = 0; j < this->y; j++)
result[i][j] = (*this)[i][j] - matrix[i][j];
return result;
}
The way to delete the memory in the destructor is the same as you wrote it in the other member functions:
delete [] _arrayofarrays;
If this crashes then you must be corrupting memory somewhere else. Try using valgrind to check for memory errors.
There are some problems with your copy constructor:
CMatrix(const CMatrix& other) : _arrayofarrays(), x(other.x), y(other.y) {
delete [] _arrayofarrays;
This is useless, there's nothing to delete yet.
_arrayofarrays = new double[x * y];
if (_arrayofarrays)
This if is useless, new will either throw an exception or return a non-null pointer, so checking if it's null is useless.
std::copy(other._arrayofarrays, other._arrayofarrays + (x * y), _arrayofarrays);
}
There are some problems with your assignment operator too:
CMatrix& operator =(const CMatrix& rval) {
delete [] _arrayofarrays;
_arrayofarrays = new double[ rval.x * rval.y];
If the new allocation throws an exception then you leave _arrayofarrays holding a dangling pointer, you should either allocate the new memory before deleting the old memory or zero the old pointer after using delete[]
I am trying to create custom array indexed from 1 using subscript operator. Getting value works fine, but I have no clue, why assign using subscript operator doesn't work.
class CEntry {
public:
CKey key;
CValue val;
CEntry(const CKey& key, const CValue& val) {
this->key = key;
this->val = val;
}
CEntry& operator= (const CEntry& b) {
*this = b;
return *this;
};
};
...
class EntriesArray {
public:
CEntry **entries;
int length;
EntriesArray(int length) {
this->length = length;
entries = new CEntry*[length];
int i;
for (i = 0; i < length + 1; i++) {
entries[i] = NULL;
}
};
CEntry& operator[] (const int index) {
if (index < 1 || index > length) {
throw ArrayOutOfBounds();
}
return *entries[index - 1];
};
};
Constructs array this way
EntriesArray a(5);
This works
a.entries[0] = new CEntry(CKey(1), CValue(1));
cout << a[1].val.value << endl;
This doesn't work
a[1] = new CEntry(CKey(1), CValue(1));
EDIT:
Using
CEntry *operator=( CEntry *orig)
it compiles okey, but gdb stops at
No memory available to program now: unsafe to call malloc warning: Unable to restore previously selected frame
with backtrace
Program received signal EXC_BAD_ACCESS, Could not access memory.
Reason: KERN_PROTECTION_FAILURE at address: 0x00007fff5f3ffff8
0x00000001000013c8 in CEntry::operator= (this=0x0, orig=0x1001008d0) at /Users/seal/Desktop/efa du2_pokus2/efa du2_pokus2/main.cpp:20
20 /Users/seal/Desktop/efa du2_pokus2/efa du2_pokus2/main.cpp: No such file or directory.
in /Users/seal/Desktop/efa du2_pokus2/efa du2_pokus2/main.cpp
At first... This:
CEntry& operator= (const CEntry& b) {
*this = b;
return *this;
};
Shouldn't work (this should result in recursive call of operator=).
The second thing is that you're trying to assign CEntry * to CEntry, this would work if you had CEntry *operator=( CEntry *orig), but I think this is bad coding practice.
This question may be related to this one.
I tried to fix your code; I believe that this is what you were trying to do:
(tested this code on g++ 5.3.0)
#include <iostream>
#include <stdexcept>
#include <string>
// Some implementation for CKey and CValue:
typedef int CKey;
struct CValue {
int value;
CValue(int value=0) : value(value) {}
};
class CEntry {
public:
CKey key;
CValue val;
CEntry(): key(0), val(0) {}
CEntry(const CKey& key, const CValue& val): key(key), val(val) {}
CEntry& operator= (const CEntry& b) {
this->key = b.key;
this->val = b.val;
return *this;
};
};
class EntriesArray {
public:
CEntry *entries;
int length;
EntriesArray(int length) {
this->length = length;
entries = new CEntry[length];
};
CEntry& operator[] (const int index) {
if (index < 1 || index > length) {
throw std::domain_error("out of bounds!");
}
return entries[index - 1];
};
};
int main(int argc, char* argv[]) {
using namespace std;
EntriesArray a(5);
// This works
a.entries[0] = CEntry(CKey(1), CValue(1));
cout << a[1].val.value << endl;
// This doesn't work
a[1] = CEntry(CKey(2), CValue(2));
cout << a[1].val.value << endl;
}
Also you might want to use a[1] as a[1].val.value e.g.:
cout << a[1] << endl;
To do this just add to this line to cEntry:
operator int() { return val.value; }
I hope it helps.
You could try replacing
CEntry& operator[] (const int index) {
if (index < 1 || index > length) {
throw ArrayOutOfBounds();
}
return *entries[index - 1];
};
with
void Add(const int index, CEntry *pEntry) {
if (index < 1 || index > length) {
throw ArrayOutOfBounds();
}
entries[index - 1] = pEntry;
};
but since you are now storing references to objects allocated on the heap (with new) you will need a destructor ~EntriesArray() to delete them all.
Because EntriesArray::operator[] returns a CEntry &, but new CEntry returns a CEntry *.
Perhaps you want a[1] = CEntry(CKey(1), CValue(1))? (no new.)
By the way, your current definition of CEntry::operator= will lead to a stack overflow.
This
return *entries[index - 1];
dereferences a NULL pointer.
You want the pointer itself to be overwritten by a[1] = new CEntry(CKey(1), CValue(1));, not the pointed-to-value.
Try this:
class EntriesArray
{
public:
int length;
CEntry **entries;
EntriesArray( int length ) : length(length), entries(new CEntry*[length]())
{
}
// defaulted special member functions are inappropriate for this class
EntriesArray( const EntriesArray& ); // need custom copy-constructor
~EntriesArray(); // need custom destructor
EntriesArray& operator=(const EntriesArray&); // need custom assignment-operator
CEntry*& operator[] (const int index) {
if (index < 1 || index > length) {
throw ArrayOutOfBounds();
}
return entries[index - 1];
}
};
Further to my comment above:
To make it work with writing new values, you probably need something like this
(I haven't double checked for off by one or ptr vs reference stuff)
CEntry& operator[] (const int index) {
if (index < 1) {
throw ArrayOutOfBounds();
}
// Add default elements between the current end of the list and the
// non existent entry we just selected.
//
for(int i = length; i < index; i++)
{
// BUG is here.
// We don't actually know how "entries" was allocated, so we can't
// assume we can just add to it.
// We'd need to try to resize entries before coming into this loop.
// (anyone remember realloc()? ;-)
entries[i] = new CEntry();
}
return *entries[index - 1];
};