Copy constructor for 2d array c++ - c++

I have managed to overload the assignment operator, so I do have a workaround for this, but it would be nice to know why I couldn't get it working.
The beginning of my arr2d class looks like:
template <class type> class arr2d {
private:
type* m_ptr;
int m_nx,m_ny;
public:
arr2d(){
m_ptr = 0;
m_nx = 0;
m_ny = 0;
}
// Default constructor creates a null array
arr2d(int nx, int ny):m_nx(nx),m_ny(ny){
m_ptr = new type [nx*ny];
if ( m_ptr==0 ){cout << "\nError allocating heap memory.\n";}
}
// // Copy constructor
// arr2d(const arr2d& rhs){
// m_ptr = new type [m_nx*m_ny];
// for(int j=0;j<m_ny;j++){
// for(int i=0;i<m_nx;i++){
// m_ptr[j*m_nx+i] = rhs.m_ptr[j*m_nx+i];
// }
// }
// }
and so on,
You can see my attempted copy constructor commented out there.
Now in my main, I would like to call the copy constructor using for instance:
arr2d b=a;
Where the b array now has the same values as a. What am I doing incorrectly?

You copy constructor is not assigning the array size. It should be something like
arr2d(const arr2d& rhs) : m_nx(rhs.m_nx), m_ny(rhs.m_ny) {
...
}

In addition to initializing m_nx and m_ny as 6502 said, you still need the template argument when declaring b. E.g.
arr2d<int> b = a;

Related

how to work with deleted object on custom vector

I know that std vectors can work with objects that are not default constructible. However, when I try to implement a slightly modified one myself, I cant seem to make such vector.
class A
{
public:
A() = delete;
A(const int &x)
:x(x)
{}
private:
int x;
};
template <typename T, int N> //default constructor of the vector
CircularBuffer<T,N>::CircularBuffer()
{
Size = 0;
Capacity =N;
Array = new T[Capacity];
Start = 0;
End = 0;
}
template <typename T, int N>
CircularBuffer<T,N>::CircularBuffer(const CircularBuffer& rhs) //copy constructor of the vector
{
Size = rhs.Size;
Capacity = rhs.Capacity;
Array = new T[Capacity];
Start = rhs.Start;
End = rhs.End;
for(int i=0;i<Capacity;i++)
{
this->Array[i] = rhs.Array[i];
}
}
template <typename T, int N> //move constructor of the vector
CircularBuffer<T,N>::CircularBuffer(CircularBuffer&& rhs)
{
rhs.Swap(*this);
}
template <typename T, int N>
void CircularBuffer<T,N>:: Swap(CircularBuffer &source)
{
swap(Size,source.Size);
swap(Capacity,source.Capacity);
swap(Start,source.Start);
swap(End,source.End);
swap(Array,source.Array);
}
When I try to make a vector with object A,
CircularBuffer<A,3> v;
error: use of deleted function ‘A::A()’
I get this error which is obviously self explanatory. Anyone can help me solve this??
The problem is here
Array = new T[Capacity];
which default constructs T objects.
std::vector uses placement new to construct objects when they are added to the vector.
Array = (T*)operator new(sizeof(T)*Capacity);
and (when you add the new item)
new(Array + i) T(...); // placement new
where ... are the arguments you wish to pass to the constructor.
See here for more details.
The issue is with the line Array = new T[Capacity];. This needs to default-initialize all members of the array, which it cannot do without a default-constructor.
std::vector gets around it by splitting allocation and construction with its allocator. I recommend you simply use std::allocator as well. Otherwise you can use placement-new to construct the elements after allocating the required memory separately.

"No viable overloaded =" nullptr

I have just started with C++ and am stuck on the move constructor. Here is my .cpp:
SimpleMatrix::SimpleMatrix(SimpleMatrix &&other_mat) {
cols = other_mat.cols;
rows = other_mat.rows;
data_ = other_mat.data_;
other_mat.cols = 0;
other_mat.rows = 0;
other_mat.data_ = nullptr; // <- Error here
}
I got No viable overloaded = error at other_mat.data_ = nullptr. What went wrong? Is it the way I initialize the matrix?
Here is the relevant parts in .hpp file:
class SimpleMatrix {
public:
SimpleMatrix(std::size_t nrows, std::size_t ncols);
SimpleMatrix(std::initializer_list<std::initializer_list<double>> data);
SimpleMatrix(SimpleMatrix&& other_mat);
SimpleMatrix& operator=(SimpleMatrix&& other_mat);
private:
std::vector<std::vector<double> > data_;
int rows, cols;
};
data_ is a vector non-pointer object, and nullptr is to initialize a pointer to be a null pointer.
You can't assign non-pointer variables to be null pointers. And C++ doesn't have any concept of null values or objects.
If you want the vector to be properly initialized I suggest you add a constructor initializer list:
SimpleMatrix::SimpleMatrix(SimpleMatrix &&other_mat)
: data_(std::move(other_mat.data_)) // Move the data from the other vector
, rows(other_mat.rows)
, cols(other_mat.cols)
{
// Clear the other matrix rows and cols
other_mat.rows = 0;
other_mat.cols = 0;
}
Or, you could rely on the rule of zero and let the compiler-generated constructors handle everything for you, which in this case it should do properly:
class SimpleMatrix {
public:
SimpleMatrix(SimpleMatrix &&) = default;
// ...
};

Unable to assign a nullptr in a forwarding reference after using the object for initializing inside move constructor

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.

Is Type Casting possible without a Conversion Constructor in C++?

For a user defined class which already has an explicit constructor accepting a single argument, Is it possible to implement the Conversion Constructor behavior in some other way.
The class in question -
class Foo
{
explicit Foo(int size);
}
Is it possible to make this code still valid -
Foo a = 3;
No. This is not possible. A constructor can only be used for one purpose, and you can't have two constructors with the same arguments that do different things.
If Foo were an array of ints like you said in your comments, it probably would be a poor design choice to have a constructor with a single argument that fills the first element of the array. It's poor design for a couple of reasons:
How do you know what size the array is going to be when Foo is initialized? Even if there were some default value, it would be a non-obvious implementation.
It's confusing. You should use the element of least surprise when designing code. I've never, ever seen a class that wraps an array have a single argument constructor that fills it's first element. It doesn't really make sense.
In C++ 11, you can use an initializer list as the second constructor.
#include <initializer_list>
#include <cstring>
class Foo {
private:
int* arr;
size_t len;
public:
explicit Foo(size_t size)
: len(size) {
arr = new int[len];
}
Foo(std::initializer_list<int> list) // Initializer list constructor
: len(list.size()) {
arr = new int[len];
size_t pos = 0;
for(auto it = list.begin(), e = list.end(); it != e; ++it) {
arr[pos++] = *it; // copy the list into your array
}
}
Foo(const Foo& other)
: len(other.len) {
arr = new int[len];
memcpy(arr, other.arr, len * sizeof*arr);
}
Foo& operator=(const Foo& other) {
if (&other != this) {
delete[] arr;
len = other.len;
arr = new int[len];
memcpy(arr, other.arr, len * sizeof*arr);
}
return *this;
}
~Foo() {
delete[] arr;
}
};
That way, you could use it like this to initialize 1 element:
Foo a = { 3 };
Or like this to create an array with "3" ints:
Foo a(3);
Or even create the whole array at once:
Foo a = { 3, 7, 10, 12, 13 };
I realize that you just used an int-array as an example, but this just shows that there are often better ways of designing code then what you propose.
Yes. Right after the definition of class Foo simply write:
struct Bar {
Bar(int){};
};
#define Foo Bar
and the required code now compiles, and calls a conversion constructor.
This seems like a bad idea, but it is possible.

Move constructor for a custom container?

is the move constructor for a class that holds a dynamically allocated array supposed to delete it? For instance I have:
template<typename T>
class MyVector
{
public:
MyVector()
{
data = new T[32];
capacity = 32;
size = 0;
}
MyVector(const MyVector<T>& o) //The basic copy constructor
{
capacity = o.capacity;
size = o.size;
data = new T[capacity];
for(int i = 0; i < _size; i++)
{
data[i] = o.data[i];
}
}
MyVector(const MyVector<T>&& o) //The move constructor
{
//What goes here?
}
private:
T* data;
unsigned int size;
unsigned int capacity;
};
I understand the move constructor is called when I do something like:
MyVector A;
A = MyVector(); //The one on the right gets a move constructor called?
So is it supposed to be identical to the copy constructor?
is the move constructor for a class that holds a dynamically allocated array supposed to delete it? For instance I have:
There is nothing to delete. The object is being constructed after all. All it should do is take the resources from the object on the RHS. It should leave the RHS in a reasonable state. For example,
MyVector(MyVector<T>&& o)
{
data = o.data;
o.data = nullptr;
capacity = o.capacity;
o.capacity = 0;
size = o.size;
o.size = 0;
}
Note that, in order for a move constructor to work, the parameter cannot be const. A move constructor modifies its argument.