A pseudo class performing matrix additon using std::unique_ptr as the data member to represent the elements. M-rows, N columns. Class is templated for type, M, N. I am trying to perform a matrix addition (C = A+B) and keep getting this error from "error: use of deleted function". I am trying to crunch the fundamentals of C++ with a complication of smart_ptrs, any pointers regarding this error is appreciated.
All the participating functions (in the error) are shown below:
class Matrix{
private:
std::unique_ptr<T[]> array=std::make_unique<T[]>(M*N);
public:
Matrix();
Matrix( const Matrix& other );
inline Matrix operator+( const Matrix& other ) const;
const Matrix& operator=( const Matrix< M, N, T >& other );
}
Matrix::Matrix(const Matrix& other)
{
*this = other;
}
inline Matrix
Matrix::operator+( const Matrix& other ) const
{
Matrix result(*this);
result += other;
return result;
}
const Matrix&
Matrix::operator=( const Matrix& other )
{
if(this != &other)
this->array = std::move(other.array);
return *this;
}
Error reporting places:
error: use of deleted function ‘matlib::Matrix<2ul, 2ul, double>::Matrix(const matlib::Matrix<2ul, 2ul, double>&)’
Matrix< M, N, T > result(*this);
error: use of deleted function ‘std::unique_ptr<_Tp [], _Dp>& std::unique_ptr<_Tp [], _Dp>::operator=(const std::unique_ptr<_Tp [], _Dp>&) [with _Tp = double; _Dp = std::default_delete<double []>]’
this->array = std::move(other.array);
Thanks in advance.
In general, most of your errors originate from that std::unique_ptr has no copy constructor. This is so that it can efficiently manage the scope of the pointer inside of it. One way around this is to create a new std::unique_ptr, and copy all individual values over.
I've written an example class for you, perhaps it can help.
#include <memory>
template <class T>
class Matrix
{
private:
std::unique_ptr<T[]> data = nullptr;
size_t height, width;
public:
Matrix(size_t h, size_t w)
: height(h), width(w)
{
if(h*w == 0) return;
data = std::make_unique<T[]>(h*w);
}
Matrix(const Matrix& other)
{
height = other.height;
width = other.width;
data = std::make_unique<T[]>(height * width);
for(size_t i = 0; i < height; i++)
{
for(size_t j = 0; j < width; j++)
{
(*this)(j, i) = other(j,i);
}
}
}
Matrix operator=( const Matrix& other )
{
if(this == &other)
{
return *this;
}
height = other.height;
width = other.width;
data.reset(std::make_unique<T[]>(other.height * other.width));
for(size_t i = 0; i < height; i++)
{
for(size_t j = 0; j < width; j++)
{
(*this)(j, i) = other(j,i);
}
}
return *this;
}
T & operator()(size_t x, size_t y)
{
//If data is nullptr then this is undefined behaviour.
//Consider adding a throw or assert here
return data[y * height + x];
}
T operator()(size_t x, size_t y) const
{
//If data is nullptr then this is undefined behaviour.
//Consider adding a throw or assert here
return data[y * height + x];
}
size_t getHeight() const
{
return height;
}
size_t getWidth() const
{
return width;
}
};
As a final statement, if what you want to do is to create matrices for mathematical purposes, I suggest you give them static sizes for performance reasons. Adding in mathematical operators to a class like this involves additional logic for the cases where dimensions are mismatched. Statically sized matrices will solve this by themselves due to their typing. You can still do it like this, but be wary of any edge cases.
Related
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 4 years ago.
Improve this question
why does this implementation for the move assignment operator give the error in the attached image
Spreadsheet::Spreadsheet(Spreadsheet&& src) noexcept :Spreadsheet(src.width, src.height) {
this->cells = src.cells; // Shallow copy of data
src.cells = nullptr; src.width = 0; src.height = 0; // Reset the source object, because ownership has been moved!
};
Spreadsheet& Spreadsheet::operator=(Spreadsheet&& rhs) noexcept {
if (this == &rhs) return *this;
for (size_t i = 0; i < width; i++) delete[] cells[i];
delete[] cells;
this->width = rhs.width; this->height = rhs.height;
this->cells = rhs.cells;
rhs.cells = nullptr; rhs.width = 0; rhs.height = 0;
return *this;
};
I attached the full resources.
the Spreadsheet class definition
SPreadsheet.h
#pragma once
#include "SpreadsheetCell.h"
class Spreadsheet
{
public:
Spreadsheet(size_t, size_t);
~Spreadsheet();//1
void setCellAt(size_t, size_t, const SpreadsheetCell&);
void verifyCoordinate(size_t, size_t) const;
void swap(Spreadsheet);
SpreadsheetCell& getCellAt(size_t, size_t);
Spreadsheet(const Spreadsheet&); //copy constructor 2
Spreadsheet& operator=(const Spreadsheet& rhs); //assignment operator 3
Spreadsheet(Spreadsheet&& src) noexcept; // Move constructor 4
Spreadsheet& operator=(Spreadsheet&& rhs) noexcept; // Move assign 5
private:
size_t width = 0;
size_t height = 0;
SpreadsheetCell** cells = nullptr;
};
the Spreadsheet class implementation
Spreadsheet.cpp
#include "stdafx.h"
#include "Spreadsheet.h"
#include<utility>
Spreadsheet::Spreadsheet(size_t width, size_t height) :width(width), height(height)
{
cells = new SpreadsheetCell*[width];
for (size_t i = 0; i < height; i++) cells[i] = new SpreadsheetCell[height];
}
void Spreadsheet::verifyCoordinate(size_t x, size_t y) const
{
(x >= width || y >= height) ? throw std::out_of_range("") : void();// void();
}
void Spreadsheet::setCellAt(size_t x, size_t y, const SpreadsheetCell& cell)
{
verifyCoordinate(x, y);
cells[x][y] = cell;
}
SpreadsheetCell& Spreadsheet::getCellAt(size_t x, size_t y) {
verifyCoordinate(x, y);
return cells[x][y];
}
Spreadsheet::~Spreadsheet()
{
for (size_t i = 0; i < width; i++) {
delete[] cells[i];
};
delete[] cells;
cells = nullptr;
}
Spreadsheet::Spreadsheet(const Spreadsheet& src) :Spreadsheet(src.width, src.height) {
for (size_t i = 0; i < width; i++)
for (size_t j = 0; j < height; j++) cells[i][j] = src.cells[i][j];
}
void Spreadsheet::swap(Spreadsheet copyOfRhsDueToBassByVal) {
std::swap(copyOfRhsDueToBassByVal.width, this->width);
std::swap(copyOfRhsDueToBassByVal.height, this->height);
std::swap(copyOfRhsDueToBassByVal.cells, this->cells);
}
Spreadsheet& Spreadsheet::operator=(const Spreadsheet &rhs) {
if (this == &rhs) return *this;//we cant use return rhs because it is const but the function header returnning a non-const;
swap(rhs); return *this;
}
Spreadsheet::Spreadsheet(Spreadsheet&& src) noexcept :Spreadsheet(src.width, src.height) {
this->cells = src.cells; // Shallow copy of data
src.cells = nullptr; src.width = 0; src.height = 0; // Reset the source object, because ownership has been moved!
};
Spreadsheet& Spreadsheet::operator=(Spreadsheet&& rhs) noexcept {
if (this == &rhs) return *this;
for (size_t i = 0; i < width; i++) delete[] cells[i];
delete[] cells;
this->width = rhs.width; this->height = rhs.height;
this->cells = rhs.cells;
rhs.cells = nullptr; rhs.width = 0; rhs.height = 0;
return *this;
};
the SpreadsheetCell class definition
SPreadsheetCell.h
#pragma once
#include <string>
#include <string_view>
#include<iostream>
using namespace std;
class SpreadsheetCell
{
public:
SpreadsheetCell() = default;
SpreadsheetCell(double initialValue);
SpreadsheetCell(std::string_view initialValue);
void setValue(double);
double getValue() const;
void setString(std::string_view);
string getString() const;
private:
string doubleToString(double) const;
double stringToDouble(std::string_view) const;
double value = 0;
};
the SpreadsheetCell class implementation
Spreadsheetcell.cpp
#include "stdafx.h"
#include "SpreadSheetCell.h"
SpreadsheetCell::SpreadsheetCell(double value) :value(value) {};
SpreadsheetCell::SpreadsheetCell(std::string_view strv) { value = stringToDouble(strv); };
void SpreadsheetCell::setValue(double value) { this->value = value; };
double SpreadsheetCell::getValue() const { return value; };
void SpreadsheetCell::setString(std::string_view str) { value = stringToDouble(str); };
string SpreadsheetCell::getString() const { return doubleToString(value); };
string SpreadsheetCell::doubleToString(double inValue) const {
return to_string(inValue);
}
double SpreadsheetCell::stringToDouble(string_view strv) const {
return strtod(strv.data(), nullptr);
}
You have several logic bugs in your code:
your allocating constructor is not looping through the 1st dimension array correctly. You are allocating an array with width number of elements, and then looping through it as if it had height number of elements instead. If width < height, you exceed the bounds of the array and corrupt memory. If width > height, you don't populate the entire array, leaving it with pointers of indeterminate values.
Your move constructor is leaking memory. It delegates to the allocating constructor instead of the default constructor, so a new array is allocated, and then it leaks that array. A move constructor should not be allocating anything at all.
your swap() is not swapping correctly. The purpose of swap() is to exchange the contents of two objects with each other, but your input parameter is being passed by value, so any object passed to it gets copied first, and then you are exchanging with the copied object, not the original object. The original object is unchanged. So you must pass the parameter by reference instead.
Also, a typical and preferable implementation for the move constructor and move assignment operator is to simply swap the contents of the moved-to and moved-from objects. Let the destructor of the moved-from object free any old resources. Don't waste time freeing the old resources before moving the new resources.
Try something more like this instead:
#pragma once
#include "SpreadsheetCell.h"
class Spreadsheet
{
public:
Spreadsheet() = default;
Spreadsheet(size_t, size_t);
Spreadsheet(const Spreadsheet &);
Spreadsheet(Spreadsheet &&) noexcept;
~Spreadsheet();
Spreadsheet& operator=(const Spreadsheet &);
Spreadsheet& operator=(Spreadsheet &&) noexcept;
SpreadsheetCell& getCellAt(size_t, size_t);
void setCellAt(size_t, size_t, const SpreadsheetCell&);
void swap(Spreadsheet &);
private:
size_t width = 0;
size_t height = 0;
SpreadsheetCell** cells = nullptr;
void verifyCoordinate(size_t, size_t) const;
};
void swap(Spreadsheet &lhs, Spreadsheet &rhs);
#include "stdafx.h"
#include "Spreadsheet.h"
#include <utility>
Spreadsheet::Spreadsheet() noexcept
: cells(nullptr), width(0), height(0)
{
}
Spreadsheet::Spreadsheet(int width, int height)
: cells(nullptr), width(width), height(height)
{
cells = new SpreadsheetCell*[width];
for (size_t i = 0; i < width; ++i)
cells[i] = new SpreadsheetCell[height];
}
Spreadsheet::Spreadsheet(const Spreadsheet &src)
: Spreadsheet(src.width, src.height)
{
for (size_t i = 0; i < width; ++i)
for (size_t j = 0; j < height; ++j)
cells[i][j] = src.cells[i][j];
}
Spreadsheet::Spreadsheet(Spreadsheet &&src) noexcept
: Spreadsheet()
{
src.swap(*this);
}
Spreadsheet::~Spreadsheet()
{
for (size_t i = 0; i < width; ++i)
delete[] cells[i];
delete[] cells;
}
Spreadsheet& Spreadsheet::operator=(const Spreadsheet &rhs)
{
if (this != &rhs)
Spreadsheet(rhs).swap(*this);
return *this;
}
Spreadsheet& Spreadsheet::operator=(Spreadsheet &&rhs) noexcept
{
rhs.swap(*this);
return *this;
}
SpreadsheetCell& Spreadsheet::getCellAt(size_t x, size_t y)
{
verifyCoordinate(x, y);
return cells[x][y];
}
void Spreadsheet::setCellAt(size_t x, size_t y, const SpreadsheetCell& cell)
{
verifyCoordinate(x, y);
cells[x][y] = cell;
}
void Spreadsheet::swap(Spreadsheet &other)
{
std::swap(cells, other.cells);
std::swap(width, other.width);
std::swap(height, other.height);
}
void Spreadsheet::verifyCoordinate(size_t x, size_t y) const
{
if (x >= width || y >= height)
throw std::out_of_range("");
}
void swap(Spreadsheet &lhs, Spreadsheet &rhs)
{
lhs.swap(rhs);
}
That being said, this code can be greatly simplified if you use std::vector instead of raw arrays:
#pragma once
#include "SpreadsheetCell.h"
#include <vector>
class Spreadsheet
{
public:
Spreadsheet() = default;
Spreadsheet(size_t, size_t);
Spreadsheet(const Spreadsheet &) = default;
Spreadsheet(Spreadsheet &&) noexcept = default;
Spreadsheet& operator=(const Spreadsheet &) = default;
Spreadsheet& operator=(Spreadsheet &&) noexcept = default;
SpreadsheetCell& getCellAt(size_t, size_t);
void setCellAt(size_t, size_t, const SpreadsheetCell&);
void swap(Spreadsheet &);
private:
std::vector<std::vector<SpreadsheetCell>> cells;
};
void swap(Spreadsheet &lhs, Spreadsheet &rhs);
#include "stdafx.h"
#include "Spreadsheet.h"
#include <utility>
Spreadsheet::Spreadsheet(int width, int height)
{
cells.resize(width);
for (size_t i = 0; i < width; ++i)
cells[i].resize(height);
}
SpreadsheetCell& Spreadsheet::getCellAt(size_t x, size_t y)
{
return cells.at(x).at(y);
}
void Spreadsheet::setCellAt(size_t x, size_t y, const SpreadsheetCell& cell)
{
cells.at(x).at(y) = cell;
}
void Spreadsheet::swap(Spreadsheet &other)
{
std::swap(cells, other.cells);
}
void swap(Spreadsheet &lhs, Spreadsheet &rhs)
{
lhs.swap(rhs);
}
So, I've looked around for an answer for a while, trying to solve this error. I think I know what the problem is, but I still can't find where or how it's happening exactly.
The error is that I'm getting a Access Violation at the location 0xcccccccc. My searching has lead me to that the problem is that I'm trying to delete a pointer that either doesn't exist, which I don't believe, or that I'm deleting a temporary variable. The problem is that I can't figure out which it is. I'm sure that once it's pointed out I'll feel stupid, but I REALLY can't seem to see this.
Pertinent Code down the callstack from the top.
// Minimum Polynomial Function
matrix matrix::MIN_POLY()
{
// Get stuff ready to find the Minimal Polynomial of the matrix
// Get our I matrix in.
col[0] = MatrixPow( *this, 0); // Goes in here <-
// ... Rest of the function ...
}
// Matrix Power Function
matrix matrix::MatrixPow(matrix& A, int pow)
{
// Create an identity matrix should the pow == 0
matrix ret(A.size[0], A.size[1]);
for(int i = 0; i < ret.size[0]; ++i)
{
ret.data[i][i] = 1;
}
for( int i = 0; i < pow; ++i)
{
// multiply it by the number of times given, and 0 returns I
ret = MatrixMult(ret, A);
}
// Ret.data exists here still and is ready to be moved and deleted.
return ret; // Seems to call Copy around here for some reason. <------
}
// Copy constructor
matrix::matrix(const matrix& other)
{
// Ret.data is gone by here for some reason.
if(data)
{
for(int i = 0; i < size[0]; ++i)
{
delete[] data[i]; // Error happens in one of these deletes <-----
}
delete[] data;
}
// Rest doesn't matter as it stops above
}
And the matrix.h since it was asked for.
#include <complex>
class matrix
{
private:
std::complex<float> **data; // [row][column]
int size[2]; // 0 is rows/ 1 is columns
public:
// Default Constructor
matrix();
// Default Destructor
~matrix();
// Default Copy
matrix(const matrix& other);
// Premade matrix constructor
matrix(int row, int col, std::complex<float>** mat);
// Assignment operator to get stuff done
matrix& operator=(const matrix& other);
// Set function for the matrix
void Set(int row, int col, std::complex<float> dat);
// Get function for the matrix, inline for ease.
std::complex<float> Get(int row, int col)
{return data[row][col];}
// Get function for the number of rows
int GetRows()
{return size[0];}
// Get function for the number of columns
int GetCol()
{return size[1];}
// Scalar Multiplication
void ScalarMult(std::complex<float> scalar);
// Matrix Multiplication
matrix MatrixMult(const matrix& A, const matrix& B);
// Matrix Power series
matrix MatrixPow(matrix& A, int pow);
// Matrix addition
matrix MatrixAdd(const matrix& lhs, const matrix& rhs);
// Row op addition
void RowOp(int row1, int row2, std::complex<float> val1, std::complex<float> val2);
// Row op Swap
void RowSwap(int row1, int row2);
// RREF function
void RREF();
// minimal Polynomial
matrix MIN_POLY();
};
You define copy constructor:
matrix::matrix(const matrix& other)
Do not initialize members:
matrix::matrix(const matrix& other)
{
//body...
}
And then perform such check:
if(data)
Which by all means cannot be false - all pointers all initialized to compiler-specific sentinel value to indicate uninitialized state and help detect bugs like this. This usually applies to debug builds, unless you enable this also in release builds.
Solution:
1. Initialize data:
matrix::matrix(const matrix& other)
: data(NULL)
{
//body...
}
2. Do not perform this unnecessary check:
matrix::matrix(const matrix& other)
: data(NULL)
{
// copy size
size[0] = other.size[0];
size[1] = other.size[1];
// Make the numbers match
data = new std::complex<float>*[size[0]];
for(int i = 0; i < size[0]; ++i)
{
data[i] = new std::complex<float>[size[1]];
for(int j = 0; j < size[1]; ++j)
{
data[i][j] = other.data[i][j];
}
}
return;
}
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.
Hi I'm implementing a matrix class in c++
I know that there are great libraries that do that like opencv but I need to do that myself.
For example if I implement the sum I can do like this
class Mat{
public:
double* data;
int rows,cols;
Mat(int r,int c):rows(r),cols(c){
data = new double[r*c];
}
};
void Sum(Mat& A,Mat& B,Mat& C){
for (int i = 0; i < A.rows*A.cols; ++i){
C.data[i] = A.data[i]+B.data[i];
}
}
int main(){
//Allocate Matrices
Mat A(300,300);
Mat B(300,300);
Mat C(300,300);
//do the sum
sum(A,B,C);
}
I would like to get something more readable like this but without losing efficiency
C = A + B
This way C is reallocated every time and I don't want that
Thank you for your time
Delay the calculation.
class MatAccess {
friend class Mat;
friend class MatOpAdd;
virtual double operator[](int index) const = 0;
};
class MatOpAdd: public MatAccess {
friend class Mat;
private:
const MatAccess& left;
const MatAccess& right;
MatOpAdd(const MatAccess& left, const MatAccess& right):
left(left), right(right) {}
double operator[](int index) const {
return left[index] + right[index];
}
};
class Mat: public MatAccess{
public:
double* data;
int rows,cols;
Mat(int r,int c):rows(r),cols(c){
data = new double[r*c];
}
MatOpAdd operator +(const MatAccess& other) {
return MatOpAdd(*this, other);
}
const Mat& operator = (const MatAccess& other) {
for(int i = 0; i < rows*cols; ++i) {
data[i] = other[i];
}
return *this;
}
private:
double operator[](int index) const {
return data[index];
}
double& operator[](int index) {
return data[index];
}
};
int main(){
//Allocate Matrices
Mat A(300,300);
Mat B(300,300);
Mat C(300,300);
//do the sum
C = A + B;
}
Now the '+' calculation will be done in the "operator="
Things I would change:
MatAccess should include the dimensions (rows,cols).
Mat adding constructors and operator= or make it not copyable
Mat::operator+ and Mat::operator= check for equal rows,col
delete memory when not used anymore or
use std::vector for simpler memory managment.
Created a bigger example here: https://gist.github.com/KoKuToru/1d23af4bbf0b2bc89893
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;
}