I am new to C++11 so i still strugle with its concepts.
Here's my problem :
I have a matrix class :
class matrix
{
private:
double** data;
size_t number_lines;
size_t number_columns;
size_t capacity_lines;
size_t capacity_columns;
public:
....
}
and i've provided a copy constructor, a move constructor...
I've overloaded the multiplication operator *(double x) to multiply matrix elements by the scalar x and return the multiplied matrix.
Here's the code :
matrix matrix::operator*(double lambda)
{
double** aux_data = new double*[number_lines];
for (size_t i = 0; i < number_lines; i++)
{
aux_data[i] = new double[number_columns];
for (size_t j = 0; j < number_columns; j++)
aux_data[i][j] = lambda*data[i][j];
}
return matrix(aux_data, number_lines, number_columns);
}
the return of the function is an rvalue reference so it invokes the move constructor. Here's the code of the move constructor :
matrix::matrix(const matrix&& moved_copy)
{
if (this != &moved_copy)
{
number_columns = moved_copy.number_columns;
number_lines = moved_copy.number_lines;
data = moved_copy.data;
}
}
The problem with this move constructor is that it performs a shallow copy and not a deep copy (like every move constructor i guess, otherwise what's the point of this move constructor) so the member data points to the object pointed by moved_copy.data, but this object is local to the operator *=() function so when the operator goes out of scope the object is gone and i have a dangling pointer. So my question is : should i perform a deep copy in the move constructor or is there way of solving this problem without doing so ?
Thank you.
No, you shouldn't make a deep copy in a move constructor. The whole point of a move constructor is to take ownership of some resource that's expensive to copy.
In this case, ownership of your data pointer can be transferred from an existing matrix to the newly constructed matrix object. But the idea is to transfer ownership, to the new object, not to share ownership with the new object. In this case that just means setting moved_copy.data to nullptr, that way it won't delete your data when it's destroyed.
matrix::matrix(matrix&& moved_copy)
{
number_columns = moved_copy.number_columns;
number_lines = moved_copy.number_lines;
data = moved_copy.data;
moved_copy.data = nullptr;
}
Notice that I also removed your if guard: there's no way to construct an object from itself, so that's not really needed for a move constructor (it can be useful for a move assignment operator though).
I also removed the const from moved_copy. Move constructors need to modify the state of the moved-from object to take ownership of its resources, so const cant' be used.
Edit: It is actually possible to construct an object from itself, but it's not something that you really need to guard against.
The problem with this move constructor is that the data member has the same value on both objects after the move, such that when the first object is deleted, the second has a pointer to deleted memory.
Change the move constructor to:
matrix::matrix(matrix&& moved_copy)
{
if (this != &moved_copy)
{
number_columns = moved_copy.number_columns;
number_lines = moved_copy.number_lines;
data = moved_copy.data;
moved_copy.number_columns = 0;
moved_copy.number_lines = 0;
moved_copy.data = nullptr;
}
}
The check if (this != &moved_copy) could be omitted, because an object is normally not constructed by moving from itself.
No you should not perform a deep copy in the move constructor. Otherwise you don't gain anything and the notion of a move constructor is broken:
matrix::matrix(matrix&& moved_copy)
: data(moved_copy.data),
number_rows(moved_copy.number_rows),
number_columns(moved_copy.number_columns),
capacity_rows(moved_copy.capacity_rows),
capacity_columns(moved_copy.capacity_columns) {
moved_copy.data = nullptr;
}
Furthermore, avoid to define binary operators as member functions because you're breaking the mathematical property of commutativity. That is, although:
matrix M;
...
matrix K = m * 2.0;
will work. The following won't:
matrix M;
...
matrix K = 2.0 * m;
Prefer to define binary operators as free functions.
matrix operator*(matrix const &m, double lambda) {
matrix out(m.aux_data, m.number_rows, m.number_columns);
...
return out;
}
matrix operator*(double lambda, matrix const &m) {
return m * lambda;
}
I am new to C++11.
So you won't mind me suggesting that you implement your matrix in terms of std::vector, as then all your move concerns are solved for you:
Here's the beginning of one implementation:
#include <vector>
#include <cstddef>
#include <iostream>
class matrix
{
private:
std::vector<double> storage_;
std::size_t row_capacity_;
std::size_t rows_;
std::size_t cols_;
std::size_t get_location(std::size_t row, std::size_t col) const
{
return row * row_capacity_ + col;
}
public:
matrix(std::size_t rows, std::size_t cols, std::size_t row_capacity, std::size_t col_capacity)
: storage_(row_capacity * col_capacity)
, row_capacity_(row_capacity)
, rows_(rows)
, cols_(cols) {}
matrix(std::size_t rows, std::size_t cols)
: matrix(rows, cols, rows, cols) {}
//
// note that all move/copy operations are automatically generated.
// "The rule of none"
//
std::size_t get_rows() const { return rows_; }
std::size_t get_cols() const { return cols_; }
std::size_t get_capacity_rows() const { return row_capacity_; }
std::size_t get_capacity_cols() const { return storage_.capacity() / row_capacity_; }
double& at(std::size_t row, std::size_t col)
{
return storage_[get_location(row, col)];
}
double const& at(std::size_t row, std::size_t col) const
{
return storage_[get_location(row, col)];
}
};
int main()
{
auto m = matrix(3, 3);
m.at(1, 1) = 2;
std::cout << m.at(1, 1) << std::endl;
std::cout << m.get_cols() << std::endl;
std::cout << m.get_rows() << std::endl;
std::cout << m.get_capacity_cols() << std::endl;
std::cout << m.get_capacity_rows() << std::endl;
}
Related
I have written a DynamicArray class in the past analogous to vector which worked.
I have also written as a demo, one where the performance is bad because it has only length and pointer, and has to grow every time. Adding n elements is therefore O(n^2).
The purpose of this code was just to demonstrate placement new. The code works for types that do not use dynamic memory, but with string it crashes and -fsanitize=address shows that the memory allocated in the addEnd() method is being used in printing. I commented out removeEnd, the code is only adding elements, then printing them. I'm just not seeing the bug. can anyone identify what is wrong?
#include <iostream>
#include <string>
#include <memory.h>
using namespace std;
template<typename T>
class BadGrowArray {
private:
uint32_t size;
T* data;
public:
BadGrowArray() : size(0), data(nullptr) {}
~BadGrowArray() {
for (uint32_t i = 0; i < size; i++)
data[i].~T();
delete [] (char*)data;
}
BadGrowArray(const BadGrowArray& orig) : size(orig.size), data((T*)new char[orig.size*sizeof(T)]) {
for (int i = 0; i < size; i++)
new (data + i) T(orig.data[i]);
}
BadGrowArray& operator =(BadGrowArray copy) {
size = copy.size;
swap(data, copy.data);
return *this;
}
void* operator new(size_t sz, void* p) {
return p;
}
void addEnd(const T& v) {
char* old = (char*)data;
data = (T*)new char[(size+1)*sizeof(T)];
memcpy(data, old, size*sizeof(T));
new (data+size) T(v); // call copy constructor placing object at data[size]
size++;
delete [] (char*)old;
}
void removeEnd() {
const char* old = (char*)data;
size--;
data[size].~T();
data = (T*)new char[size*sizeof(T)];
memcpy(data, old, size*sizeof(T));
delete [] (char*)old;
}
friend ostream& operator <<(ostream& s, const BadGrowArray& list) {
for (int i = 0; i < list.size; i++)
s << list.data[i] << ' ';
return s;
}
};
class Elephant {
private:
string name;
public:
Elephant() : name("Fred") {}
Elephant(const string& name) {}
};
int main() {
BadGrowArray<int> a;
for (int i = 0; i < 10; i++)
a.addEnd(i);
for (int i = 0; i < 9; i++)
a.removeEnd();
// should have 0
cout << a << '\n';
BadGrowArray<string> b;
b.addEnd("hello");
string s[] = { "test", "this", "now" };
for (int i = 0; i < sizeof(s)/sizeof(string); i++)
b.addEnd(s[i]);
// b.removeEnd();
cout << b << '\n';
BadGrowArray<string> c = b; // test copy constructor
c.removeEnd();
c = b; // test operator =
}
The use of memcpy is valid only for trivially copyable types.
The compiler may even warn you on that, with something like:
warning: memcpy(data, old, size * sizeof(T));
writing to an object of non-trivially copyable type 'class string'
use copy-assignment or copy-initialization instead [-Wclass-memaccess]
Note that your code do not move the objects, but rather memcpy them, which means that if they have for example internal pointers that point to a position inside the object, then your mem-copied object will still point to the old location.
Trivially Copyable types wouldn't have internal pointers that point to a position in the object itself (or similar issues that may prevent mem-copying), otherwise the type must take care of them in copying and implement proper copy and assignemnt operations, which would make it non-trivially copyable.
To fix your addEnd method to do proper copying, for non-trivially copyable types, if you use C++17 you may add to your code an if-constexpr like this:
if constexpr(std::is_trivially_copyable_v<T>) {
memcpy(data, old, size * sizeof(T));
}
else {
for(std::size_t i = 0; i < size; ++i) {
new (data + i) T(std::move_if_noexcept(old[i]));
}
}
In case you are with C++14 or before, two versions of copying with SFINAE would be needed.
Note that other parts of the code may also require some fixes.
I am trying to get used to the move constructor and in one of tutorials it was told that it is always a good practice to initialize the original refence to the nullptr after copying the content using a forwarding reference.
#include <iostream>
#include <vector>
using namespace std;
template<typename T>
class Matrix{
std::vector<std::vector<T>> data;
public :
Matrix(const std::vector<std::vector<T>>& vector2D){ //Copy constructor for deep copy
// Put some error handling to check the validity of vector2D
std::cout<<"Copy Constructor Called ...\n";
__uint32_t numRow = vector2D.size();
__uint32_t numColumn = vector2D[0].size();
data.resize(numRow,std::vector<T>(numColumn));
for(auto row = 0u;row < numRow; ++row){
for(auto column = 0u; column < numColumn; ++column){
data[row][column] = vector2D[row][column];
}
}
}
Matrix(std::vector<std::vector<T>>&& vector2D){ //Move constructor for shallow copy
// Put some error handling to check the validity of vector2D
std::cout<<"Move Constructor Called ...\n";
data = vector2D;
//vector2D = nullptr; // we should assign the original reference to null in my knowledge
}
void displayVector(){
__uint32_t numRow = data.size();
__uint32_t numColumn = data[0].size();
for(auto row = 0u;row < numRow; ++row){
for(auto column = 0u; column < numColumn; ++column){
std::cout<<data[row][column]<<"\t";
}std::cout<<std::endl;
}
}
Matrix operator+ (const Matrix& rhs){
// Have to complete
return data;
}
Matrix operator* (const Matrix& rhs){
// Have to complete
return data;
}
};
int main() {
cout << "!!!Hello World!!!" << endl; // prints !!!Hello World!!!
//Matrix<int> m1(std::vector<std::vector<int>>{{1,2},{3,4}});
Matrix<int> m1({{1,2},{3,4}});
m1.displayVector();
std::vector<std::vector<int>> myVector{{5,6},{7,8}};
Matrix<int> m2(myVector);
m2.displayVector();
return 0;
}
But, when I am attempting to put nullptr inside vector2D inside the move constructor the compiler is complaining saying
no known conversion for argument 1 from std::nullptr_t to std::initializer_list of std::vector.
What are a few possibly correct ways to do such an initialization.
Two things here:
Matrix(std::vector<std::vector<T>>&& vector2D){ //Move constructor for shallow copy
// Put some error handling to check the validity of vector2D
std::cout<<"Move Constructor Called ...\n";
data = vector2D;
//vector2D = nullptr; // we should assign the original reference to null in my knowledge
}
1) you should move the received vector, to invoke vectors move assignment operator:
data = std::move(vector2D);
2) no need to manually set the vector to nullptr, it's state is already properly set.
The vector you move from is left in an "unknown, but valid state", so you can do with it anything that does not assume precondition (reasign, check size, check for emptiness, etc., you can't however expect it to have valid values inside).
As the other answer mentioned, you should also directly initialize the data, instead of doing it in the constructor body, so finally the constructor should be implemented this way probably:
Matrix(std::vector<std::vector<T>>&& vector2D) : data(std::move(vector2D)) {}
You should be writing that:
// Not a move constructor, that would be `Matrix(Matrix&&)`
Matrix(std::vector<std::vector<T>>&& vector2D) : data(std::move(vector2D)) {
std::cout<<"Move non-Constructor Called ...\n";
}
Vectors cannot be nullptr, but they can be empty - data(std::move(vector2D)) will empty the original vector and move the contents to data.
I have overloaded assignment operator for the class with a 2D array, but in order to do memory management and resizing correct I have to delete previous matrix first, then construct a new one, and only then I can start assigning.
Matrix& Matrix::operator = (const Matrix& m1){
for (int i = 0; i < m_rows; ++i)
delete[] m_matrix[i];
delete[] m_matrix;
m_matrix = new double*[m1.rows()];
for (int i = 0; i < m1.rows(); ++i)
m_matrix[i] = new double[m1.cols()]();
for (int k = 0; k < m1.rows(); ++k)
for (int j = 0; j < m1.cols(); ++j)
m_matrix[k][j] = m1.m_matrix[k][j];
m_rows = m1.rows();
m_cols = m1.cols();
return *this;
}
In fact, this part is destructor of my class:
for (int i = 0; i < m_rows; ++i)
delete[] m_matrix[i];
delete[] m_matrix;
And this part is similar to a constructor:
m_matrix = new double*[m1.rows()];
for (int i = 0; i < m_rows; ++i)
m_matrix[i] = new double[m1.cols()]();
What annoys me is that I have to copy constructors' and destructors' code in the assignment function (and some other functions too!) to make it work properly. Is there a better way to write it?
The ideal improvement would be Matrix& Matrix::operator=(const Matrix&) = default;.
If you switch to using std::vector for matrix storage you won't need to implement the copy/move constructors/assignments and destructor at all.
If what you are doing is a programming exercise, create your own dynamic array and use that in the implementation of your matrix.
I cannot recommend enough watching Better Code: Runtime Polymorphism by Sean Parent, he makes an effective demonstration of why you should strive to write classes that do not require non-default implementations of copy/move constructors/assignments and destructor.
Example:
template<class T>
class Matrix
{
std::vector<T> storage_;
unsigned cols_ = 0;
public:
Matrix(unsigned rows, unsigned cols)
: storage_(rows * cols)
, cols_(cols)
{}
// Because of the user-defined constructor above
// the default constructor must be provided.
// The default implementation is sufficient.
Matrix() = default;
unsigned columns() const { return cols_; }
unsigned rows() const { return storage_.size() / cols_; }
// Using operator() for indexing because [] can only take one argument.
T& operator()(unsigned row, unsigned col) { return storage_[row * cols_ + col]; }
T const& operator()(unsigned row, unsigned col) const { return storage_[row * cols_ + col]; }
// Canonical swap member function.
void swap(Matrix& b) {
using std::swap;
swap(storage_, b.storage_);
swap(cols_, b.cols_);
}
// Canonical swap function. Friend name injection.
friend void swap(Matrix& a, Matrix& b) { a.swap(b); }
// This is what the compiler does for you,
// not necessary to declare these at all.
Matrix(Matrix const&) = default;
Matrix(Matrix&&) = default;
Matrix& operator=(Matrix const&) = default;
Matrix& operator=(Matrix&&) = default;
~Matrix() = default;
};
The canonical implementation of the assignment operator leverages existing functionality (copy/move ctor, dtor, andswap(); note that using a non-specialized std::swap() would be bad). It looks like this:
T& T::operator= (T val) {
val.swap(*this);
return *this;
}
It nicely avoids reimplementing otherwise existing logic. It also deals gracefully with self-assignment which is a problem in your original code (it will do work but self-assignment is generally rather uncommon; optimizing it with a check against self-assignment typically pessimizes code).
The argument is passed by value to take advantage of copy elision.
The primary caveats with this approach are below. In general I prefer the canonical implementation as it is generally more correct and the outlined issue are often not really that relevant (e.g., when the object was just created anyway the transferred memory is actually "hot").
It does not attempt to reuse already allocated and possibly "hot" memory. Instead it always uses new memory.
If the amount of held data is huge, there are copies temporarily held which may exceed system limits. Reusing existing memory and/or release memory first would both address this issue.
I have a class meant to implement a matrix, here:
template<typename Comparable>
class Matrix {
private:
std::size_t num_cols_;
std::size_t num_rows_;
Comparable **array_;
public:
Matrix();
~Matrix(); // Destructor
Matrix(const Matrix<Comparable> & rhs);// Copy constructor
Matrix(Matrix<Comparable> && rhs); // Move constructor
Matrix<Comparable> & operator= (const Matrix<Comparable> & rhs);// Copy assignment
Matrix<Comparable> & operator= (Matrix<Comparable> && rhs); // Move assignment
template<typename buh> friend std::ostream &operator<< (std::ostream &os, Matrix<buh> &rhs);
void ReadMatrix();
};
(Vectors aren't an option for this particular problem.)
The array_ member in particular holds the matrix itself, and is populated using the following code:
array_ = new Comparable*[num_rows_];
for (int i = 0; i < num_rows_; ++i) {
array_[i] = new Comparable[num_cols_];
};
for(int i = 0;i < num_rows_; ++i) {
std::cout << "Enter items for row " << i << "." << std::endl;
for(int j = 0;j < num_cols_; ++j) {
std::cin >> array_[i][j];
}
}
I can fill the array with values and access them, and my copy constructor and move assignment operator are functional, but the move assignment operator throws out a strange bug. here's the definition.
template<typename Comparable>
Matrix<Comparable>& Matrix<Comparable>::operator= (Matrix<Comparable> && rhs) {
delete[] array_;
array_ = new Comparable*[rhs.num_rows_];
for(int i = 0;i < rhs.num_rows_;++i) {
std::swap(array_[i],rhs.array_[i]);
rhs.array_[i] = nullptr;
}
rhs.num_cols_ = 0;
rhs.num_rows_ = 0;
rhs.array_ = nullptr;
return *this;
}
Take the statement a = std::move(b);. If b is of a different size than a, the matrix data is deformed by the move. If b has more columns than a, the extra columns will be cut off; if b has fewer rows than a, the excess rows will remain in a; if a has more columns or rows than b, the excess columns will display memory address where there should be nothing at all. Is this a simple bug? Is there a problem with way I create the arrays? Any insight into what's causing this is appreciated.
"Move assign" doesn't mean "carefully modify the passed in object to become some sort of 'empty' value", it means "it's fine to modify the passed in object".
Move assignment here should have a very simple implementation: just swap.
template<typename Comparable>
Matrix<Comparable>& Matrix<Comparable>::operator= (Matrix<Comparable> && rhs) {
using std::swap;
swap(array_, rhs.array_);
swap(num_cols_, rhs.num_cols_);
swap(num_rows_, rhs.num_rows_);
return *this;
}
Not sure why you are using new Comparable* in your move assignment operator. The idea of a move assignment is to move the resources, not make new ones.
Your code could look like:
delete[] array_;
array_ = rhs.array_;
rhs.array_ = nullptr;
num_cols_ = rhs.num_cols_;
num_rows_ = rhs.num_rows_;
return *this;
However, consider using the copy-and-swap idiom. It's not always the most efficient option but it is a good starting point if you're not a guru.
Note: If you really want to use a pointer to pointer to implement your matrix, use vector<vector<Comparable>> instead. All the work is done for you; your code is just reinventing the wheel.
Usually it is simpler and more effective to represent a matrix with one contiguous allocation, instead of a separate allocation for each row, so you may want to give that idea some thought.
I have implemented a Matrix class with a move assignment as
template <typename OutType>
class Matrix
{
public:
int Rows_; // number of Rows
int Columns_; // number of Columns
OutType *data_; // row Major order allocation
// STUFF
Matrix<OutType> & operator=(Matrix<float>&& other) {
swap(other);
return *this;
}
void swap(Matrix<float>& other) {
int t_Rows_ = Rows_; Rows_ = other.Rows_; other.Rows_ = t_Rows_;
int t_Columns_ = Columns_; Columns_ = other.Columns_; other.Columns_ = t_Columns_;
float* t_ptr = data_;
data_ = other.data_;
other.data_ = t_ptr; }
}
in order to implement the B=f(A); syntax, as suggested in
C++: Implementing B=f(A), with B and A arrays and B already defined
As possible function, I'm considering the FFT, implemented as
Matrix<float> FFT(const Matrix<float> &in)
{
Matrix<float> out(in.GetRows(),in.GetColumns());
// STUFF
return out;
}
Is there any room for further efficiency improvements? Is there any further trick to improve, for example, the move assignment or the swap function?
EDIT: NEW SOLUTION FOLLOWING KONRAD RUDOLPH'S COMMENT
Matrix & operator=(Matrix&& other) {
std::swap(Rows_, other.Rows_);
std::swap(Columns_, other.Columns_);
std::swap(data_, other.data_);
std::cout << "move assigned \n";
return *this;
}
I recommend implementing move-assignment and move-construction for your class:
Matrix( Matrix<OutType> &&that ) noexcept
: Rows_(that.Rows_)
, Cols_(that.Cols_)
, data_(that.data_)
{
that.Rows_ = that.Cols_ = 0;
that.data_ = nullptr;
}
Matrix<OutType> &operator=( Matrix<OutType> &&that ) noexcept {
using std::swap;
swap( Rows_, that.Rows_ );
swap( Cols_, that.Cols_ );
swap( data_, that.data_ );
return *this;
}
If you implement move operations (construction and assignment) like this, std::swap should work great for your code, and you don't need to provide your own. If you do want to provide your own implementation of swap, I recommend providing it as a two-argument friend function so that it can be found through Argument Dependent Look-up. I also recommend calling swap (and all other functions) without namespace qualifications, as shown above, so that ADL is not suppressed (unless, for some reason, you really need to specify exactly which function is called, and an overload customized for the specific type would be wrong). ADL is especially valuable when dealing with templated code. If you call std::swap with the std:: qualifier, you significantly reduce the opportunity for user-defined types to provide a more efficient swap implementation.