I have a project in which I created an abstract class that represents a shape. I have a circle and a quad inherited from a shape and square inherited from a quad.
Finally I have a class called allShapes that has a polymorphic array of Shape ** pointers and its size.
I need to implement the + operator, which receives an allShapes object and returns a new allShape with all elements located at this and other.
When I copy the part of this, the copy is done correctly but when I copy the parts from other I think it does not copy because when the function is finished when it comes to destruction I jump to an error that I am trying to delete blank content. what did I do wrong?
allShapes allShapes::operator+(const allShapes & other) const
{
allShapes newS;
newS._size = (this->getSize() + other.getSize());
int k = 0;
newS._arr = new Shape*[newS.getSize()];
for (int i = 0; i < this->getSize(); i++)
{
newS._arr[i] = this->_arr[i];
}
for (int j = this->getSize(); j < newS.getSize(); j++)
{
newS._arr[j] = other._arr[k++]; //i think here is the problem
}
return newS;
}
edit: i add the others methods that someone asks:
allShapes::allShapes(const allShapes & other) //copy constructor
{
this->_size = other.getSize();
this->_arr = new Shape*[other.getSize()];
for (int i = 0; i < other.getSize(); i++)
{
this->_arr[i] = other._arr[i];
}
}
allShapes::~allShapes()//destructor to all elements
{
if (this->_arr != NULL)
{
for (int i = 0; i < this->_size; i++)
{
delete this->_arr[i];
}
delete[] this->_arr;
}
}
class allShapes {
private:
Shape ** _arr;
int _size;
what did I do wrong?
You used Shape ** to denote ownership of multiple Shape-derived objects, and copy the pointers. Whichever allShapes object that is destroyed first invalidates all the Shape *s in the other copy.
There are two possibilities for making it hard to go wrong. Either each allShapes has it's own copy of each Shape it holds, or they all share ownership. This is best expressed via a collection of either std::unique_ptr<Shape> for the former, or std::shared_ptr<Shape> for the latter.
class allShapes {
private:
std::vector<std::shared_ptr<Shape>> data;
public:
allShapes operator+(const allShapes & other)
{
allShapes copy = *this;
copy.data.insert(copy.data.end(), other.data.begin(), other.data.end());
return copy;
}
// compiler generated special members are correct
};
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 have been learning C++ for some time and started writing a larger project just to realise that whatever I was thinking about C++ is wrong.
Basically I have a class called DenseVector which holds doubles. I want to move, copy construct etc that vector to use it with vectors etc.
The class looks something like this:
DenseVector.h:
class DenseVector {
private:
double* values = nullptr;
int size;
public:
DenseVector();
DenseVector(int size);
DenseVector(double x, double y);
DenseVector(double x, double y, double z);
DenseVector(const DenseVector &other);
DenseVector(DenseVector &&other);
virtual ~DenseVector();
DenseVector& operator=(const DenseVector& other);
DenseVector& operator=(DenseVector&& other);
}
DenseVector.cpp
DenseVector::DenseVector() : DenseVector(3) {}
DenseVector::DenseVector(int size) : size(size) {
this->values = new double[size + size % 2]{0};
}
DenseVector::DenseVector(double x, double y) {
this->size = 2;
this->values = new double[2]{x,y};
}
DenseVector::DenseVector(double x, double y, double z) {
this->size = 3;
this->values = new double[4]{x,y,z};
}
DenseVector::DenseVector(const DenseVector &other) : size(other.size) {
this->values = new double[size + size % 2]{0};
memcpy(values, other.values, size * sizeof(double));
}
DenseVector::DenseVector(DenseVector &&other) : values(other.values), size(other.size){}
DenseVector::~DenseVector() {
if(values != nullptr){
delete values;
values = nullptr;
}
}
DenseVector &DenseVector::operator=(const DenseVector &other) {
this->size = other.size;
memcpy(values, other.values, size * sizeof(double));
return *this;
}
DenseVector &DenseVector::operator=(DenseVector &&other) {
this->values = other.values;
this->size = other.size;
return *this;
}
I assume its a very straight forward implementation for mathematical vectors in C++. Note that size of the array internally is always a multiple of two. This is due to speedups using AVX/SSE which is not part of this question.
Basically I keep getting a Segmentation fault inside the deconstructor when trying to delete the value and I have no idea why this keeps on happening!
And example would be the following snippet:
std::vector<DenseVector> positions;
for(int i = 0; i < 10; i++){
positions.push_back({1,2,3});
}
This really confuses me and I would be very very happy if someone could help me with this problem as this had happened to me before many times in other programs.
Also what would be the difference between using push_back and emplace_back in this case? Should one prefer one over the other one? I do not understand at which point objects will be created, moved, deleted etc.
Greetings
Finn
I can see at least two problems:
DenseVector &DenseVector::operator=(const DenseVector &other) {
this->size = other.size;
memcpy(values, other.values, size * sizeof(double));
return *this;
}
You are not checking whether you have enough space in this->values. If this->size is smaller than other->size, you need to reallocate.
DenseVector::DenseVector(DenseVector &&other) : values(other.values),
size(other.size){}
DenseVector &DenseVector::operator=(DenseVector &&other) {
this->values = other.values;
this->size = other.size;
return *this;
}
In both cases you end up with two pointers pointing to the same memory. Now when you destroy both vectors, you get a double delete. You need to have other->values = nullptr; in both functions.
A better way to fix both issues it to use std::vector and rely on the rule of zero.
You have several problems.
The one in destructor is that delete should match with new not new[].
You have to use delete[] here:
DenseVector::~DenseVector() {
delete [] values;
}
Note: Deleting null pointer is fine, no test needed. setting values to nullptr is useless too, as reading the value after the object is destroyed is UB anyway.
Your move constructor doesn't move, but shallow copy, so you will have double delete, it should be
DenseVector::DenseVector(DenseVector &&other) /*noexcept*/: values(other.values), size(other.size)
{
other.vlalues = nullptr;
other.size = 0;
}
You have many problems:
first you should move objects in move-ctor and leave the moved-from in a valid state:
DenseVector::DenseVector(DenseVector &&other) : values(std::move(other.values)), size(std::move(other.size))
{
other.values = nullptr;
}
You should use ctor-init list to initialize member data rather than assign to them inside ctor body after being default-init in ctor-init-list.
DenseVector::DenseVector(int size) : size(size),
values(new double[size]{0})
{
}
Also in the destructor you are freeing an array of doubles with delete which is Undefined Behavior so use it this way:
delete[] values;
You don't need to check in the dtor whether values is nullptr or not:
delete[] values;
I am trying to create a constructor, destructor, and copy constructor for a matrix class, and I am not so sure if I am doing it well.
In particular, I am not sure about 2 things:
Is the destructor suppose to free memory also for the memory that is allocated in the copy-constructor?
As to the line Mat[i][j]=other[i][j] (see in the code below), I wonder if I should write Mat[i][j]=other.Mat[i][j] instead?
class Matrix
{
private:
int rows;
int cols;
int **Mat;
public:
Matrix (const int &rows,const int &cols);
Matrix (const Matrix &other);
~Matrix ();
};
Matrix::Matrix(const int &n_rows,const int &n_cols) //constructor of class Matrix
{
rows=n_rows;
cols=n_cols;
Mat=new int* [cols];
for(int i =0;i<rows;i++)
Mat[i]=new int[cols];
for(int i=0;i<rows;i++)
for(int j=0;j<cols;j++)
Mat[i][j]=0;
}
Matrix::~Matrix () //destructor
{
for(int i =0;i<rows;i++)
delete Mat[i];
delete[] Mat;
}
Matrix::Matrix(const Matrix &other) //copy constructor
{
cols=other.cols;
rows=other.rows;
Mat=new int* [other.rows];
for(int i =0;i<other.rows;i++)
Mat[i]=new int[other.cols];
for(int i=0;i<other.rows;i++)
for(int j=0;j<other.cols;j++)
Mat[i][j]=other[i][j];
}
1) I think deconstructor erases only object that belongs to, because copy-constructed objects have their own destructor.
2) Yes, Mat[i][j] = other.Mat[i][j] is right, but if you want your program to be bit faster, try using pointers (it isn't easy at first time, I know, but when you get used to it isn't that hard ^^)
A constructor is a class operation that initializes an object instance of a specific class.
The object creation involves several operations such as:
Allocating memory to store the structure for the new object
Initializing the object's attributes properly.
A copy constructor is a special case of constructor, that takes an instance of the same class as an input parameter. - It is still a constructor, performing the same operations mentioned above.
A destructor is a class operation which is responsible for finalizing an object when it is not to be used anymore.
An object can be constructed using any constructor it defines, either ordinary or copy constructor. When you delete that object, any memory allocated by your class should be deallocated in the destructor.
Hope this helps.
As for your code related question; when invoking a copy constructor, you pass an existing object instance of the class. Since you don't have any operator overloading implemented, you access the object's property like you said in #2.
I am not sure if this answers your questions fully, but I hope it helps.
Yes. Anything the object still has allocated should be freed in the destructor. It doesn't matter which constructor allocated it.
Yes, you need to use Mat[i][j]=other.Mat[i][j], especially since you haven't defined any operator[] for your class.
You also need to add a copy assignment operator, per the "Rule of Three", which basically states:
If a class requires a user-defined destructor, a user-defined copy constructor, or a user-defined copy assignment operator, it almost certainly requires all three.
Try this:
class Matrix
{
private:
int rows;
int cols;
int **Mat;
public:
Matrix (int n_rows, int n_cols);
Matrix (const Matrix &other);
~Matrix ();
Matrix& operator=(const Matrix &rhs);
// alternatively:
// Matrix& operator=(Matrix rhs);
};
Matrix::Matrix(int n_rows, int n_cols)
{
rows = n _rows;
cols = n_cols;
Mat = new int*[rows];
for(int i = 0; i < rows; ++i)
{
Mat[i] = new int[cols];
for(int j = 0; i < cols; ++j)
Mat[i][j] = 0;
}
}
Matrix::Matrix(const Matrix &other)
{
rows = other.rows;
cols = other.cols;
Mat = new int*[rows];
for(int i = 0; i < rows; ++i)
{
Mat[i] = new int[cols];
for(int j = 0; j < cols; ++j)
Mat[i][j] = other.Mat[i][j];
}
}
Matrix::~Matrix()
{
for(int i = 0; i < rows; ++i)
delete Mat[i];
delete[] Mat;
}
Matrix& Matrix::operator=(const Matrix &rhs)
{
if (&rhs != this)
{
Matrix temp(rhs);
std::swap(Mat, temp.Mat);
std::swap(rows, temp.rows);
std::swap(cols, temp.cols);
}
return *this;
}
// alternatively:
/*
Matrix& Matrix::operator=(Matrix rhs)
{
std::swap(Mat, rhs.Mat);
std::swap(rows, rhs.rows);
std::swap(cols, rhs.cols);
return *this;
}
*/
A better solution is to not use new[]/delete[] directly at all. Use std::vector instead, and let it handle everything for you, thus allowing your class to follow the "Rule of Zero":
Classes that have custom destructors, copy/move constructors or copy/move assignment operators should deal exclusively with ownership (which follows from the Single Responsibility Principle). Other classes should not have custom destructors, copy/move constructors or copy/move assignment operators.
class Matrix
{
private:
std::vector<std:vector<int> > Mat;
public:
Matrix (int n_rows, int n_cols);
};
Matrix::Matrix(int n_rows, int n_cols)
{
Mat.resize(n_rows);
for(int i = 0; i < n_rows; ++i)
Mat[i].resize(n_cols, 0);
/* alternatively:
Mat.resize(n_rows, std::vector<int>(n_cols, 0));
*/
}
The following code constitutes a MCVE, this reproduces the problem I want to ask about but it's not the real code. The real code is quite more complicated so that's why I wrote this for a demonstration of the problem.
The key feature I am looking for is to be able to grow a dynamically allocated array, please do not suggest using the stl because it's explicitly forbidden. This code is for educational purpose and thus there are restrictions.
#include <cstring>
#include <iostream>
class Value
{
public:
Value(int value = 0);
Value(const Value &value);
Value &operator =(const Value &other);
~Value();
operator int() {return *m_absurdPointer;}
private:
int *m_absurdPointer;
};
Value::Value(int value) :
m_absurdPointer(new int[1])
{
*m_absurdPointer = value;
}
Value::Value(const Value &value)
{
m_absurdPointer = new int[1];
memcpy(m_absurdPointer, value.m_absurdPointer, sizeof(*m_absurdPointer));
}
Value &Value::operator =(const Value &other)
{
m_absurdPointer = new int[1];
memcpy(m_absurdPointer, other.m_absurdPointer, sizeof(*m_absurdPointer));
return *this;
}
Value::~Value()
{
delete[] m_absurdPointer;
}
class ValueArray
{
public:
ValueArray();
~ValueArray();
void append(const Value &value);
void show() const;
private:
Value *m_array;
unsigned int m_capacity;
unsigned int m_length;
};
ValueArray::ValueArray() :
m_array(nullptr)
, m_capacity(0)
, m_length(0)
{
}
ValueArray::~ValueArray()
{
delete[] m_array;
}
void
ValueArray::append(const Value &value)
{
if (m_length >= m_capacity)
{
Value *newarray;
unsigned int unitSize;
unitSize = 1;
newarray = new Value[m_capacity + unitSize];
if ((m_capacity > 0) && (m_array != nullptr))
memcpy(newarray, m_array, m_capacity * sizeof(*m_array));
delete[] m_array;
m_array = newarray;
m_capacity += unitSize;
}
m_array[m_length++] = value;
}
void
ValueArray::show() const
{
for (size_t i = 0 ; i < m_length ; ++i)
std::cout << static_cast<int>(m_array[i]) << std::endl;
}
int
main(void)
{
ValueArray example;
for (int i = 0 ; i < 10 ; ++i)
example.append(Value(i));
example.show();
return 0;
}
It causes as you can see a double free issue, because the delete[] m_array; calls the destructor of the class Value after it has copied the values to the re-newed array.
I tried to do this with malloc()/realloc() but I need the destructor of Value() to be called so new is mandatory because I can't use free().
How to prevent this?, if I remove the delete[] m_absurdPointer; the double free would be gone of course but there would be a memory leak.
You basically want to implement an own vector class, right?
OK, first things first: As far as I know you cannot grow previously allocated memory. At least not with the standard allocator.
So you need to allocate a new, larger chunk of memory.
You can do this the standard way, using new:
Type * newdata = new Type[size];
In this case the constructor of the class Type will be called for each new element, which is size times.
To get your old data into that new array you need to copy or move it there:
for (size_t it = 0; it < oldsize; ++it) {
newdata[it] = olddata[it];
// newdata[it] = std::move(olddata[it]);
}
This is what std::copy resp. std::move are doing. (You could also use std::swap inside a loop.)
For that to work the Type class needs both a default constructor and a valid implementation of copy or move assignment.
You're using memcpy. In C++, this is generally a bad idea: Your implemented assignment operator isn't called, Therefore both the objects in your old array and the raw copies are using the same pointer, which is why you get that double free, obviously.
You could also allocate raw memory and use placement new to copy or move construct the new objects from the old ones:
void * memory = new char[size * sizeof(Type)];
for (size_t it = 0; it < oldsize; ++it) {
new (memory + it * sizeof(Type)) Type(olddata[it]); // copy
}
The above is only an example, for real code you need to consider alignment, too.
Finally, I'm sure you can somehow trick the default allocator to free your (old) memory without destructing the objects within, this allowing you to use the raw copy memcpy made. Though this would be a hack and could break on complex classes, it's not the C++ way of doing this.
The idiomatic way is to copy or move the old objects to the new storage (with either assignment or construction).
You should use the move-constructor if you have to stick with an vector-like implementation of ValueArray:
class Value
{
public:
Value(int value = 0);
Value(const Value &value);
Value(Value&& val);
Value &operator =(const Value &other);
Value &operator =(Value&& other);
~Value();
operator int() {return *m_absurdPointer;}
private:
int *m_absurdPointer;
};
Value::Value(Value&& o) : m_absurdPointer(o.m_absurdPointer) {
o.m_absurdPointer = nullptr;
}
Value &operator =(Value&& o) {
delete[] this->m_absurdPointer;
this->m_absurdPointer = o.m_absurdPointer;
o.m_absurdPointer = nullptr;
}
void
ValueArray::append(const Value &value)
{
if (m_length >= m_capacity)
{
Value *newarray;
unsigned int unitSize;
unitSize = 1;
newarray = new Value[m_capacity + unitSize];
if ((m_capacity > 0) && (m_array != nullptr)) {
std::move(m_array, m_array + m_length, newarray);
}
delete[] m_array;
m_array = newarray;
m_capacity += unitSize;
}
}
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.