C++ rule of 5 - segmentation fault on custom destructor - c++

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;

Related

Core Dumped due to delting an array

I'm trying to make a copy constructor with low level arrays and I'm getting a core dumped error when using delete, can't find out a solution because I'm not able to use std::vector to make this.
Can you guys help me ?? =)
#include<iostream>
#include<string>
#include<initializer_list>
class Vector{
size_t n;
double* datos;
public:
Vector(size_t n_,double);
Vector(const std::initializer_list<double>&l);
void show();
~Vector(){
delete[] datos;
}
Vector(Vector&& other):n(other.n){
delete [] datos;
datos =other.datos;
other.datos =nullptr;
}
Vector(const Vector& v);
Vector operator = (const Vector& v);
};
/* --------------------------- METODOS DE LA CLASE -------------------------- */
Vector::Vector(const Vector&v){
delete[]datos; //CORE DUMPED
n=v.n;
datos = new double[n];
for (size_t i = 0; i < n; i++)
{
datos[i] = v.datos[i];
}
std::cout<<std::endl;
}
Vector Vector::operator = (const Vector& v){
delete [] datos;//CORE DUMPED
n = v.n;
for (size_t i = 0; i < n; i++)
{
datos[i] = v.datos[i];
}
return *this;
}
Vector::Vector(const std::initializer_list<double>&l):n(l.size()),datos(new double[n]){
size_t j= 0;
for (auto i:l)
{
datos[j]=i;
++j;
}
}
void Vector::show(){
for (size_t i = 0; i < n; i++)
{
std::cout<<datos[i]<<", ";
}
std::cout<<std::endl;
}
Vector::Vector(size_t n_,double d=0):n(n_),datos(new double[n]){
if (n < 1)
{
throw std::invalid_argument("Wrong size!");
}
for (size_t i = 0; i < n; i++)
{
datos[i] = d;
}
}
int main(){
Vector b={2,3,4,5,6},a(3);
a=b;
a.show();
}
Using POP OS 21.04 (just in case this can help).
Please don't be rough with me I'm a junior programmer trying to pass September's exams =(
Vector::Vector(const Vector&v){
delete[]datos; //CORE DUMPED
You didn't initialise datos, so its value is indeterminate. When you delete an indeterminate pointer, then the behaviour of the program is undefined. "CORE DUMPED" is one possible behaviour that you may observe.
You are issuing unneeded calls to delete [] datos in your constructors (move and copy).
Since datos is uninitialized, calling delete [] on an uninitialized pointer leads to undefined behavior -- in your case, your program crashes.
Since the objects are being constructed, there is no reason to issue a delete [] on the pointer, since the object this is brand new.
Simply remove the call to delete [] datos; from the constructors. Whether this is the only problem is another story, but it is an existing one.
In addition, your assignment operator:
Vector Vector::operator = (const Vector& v)
is also incorrect. It fails to allocate new memory after the delete [] call, thus the for loop that is written will write into unallocated memory. Also, it should return a reference to the current Vector object, not a brand new Vector object.
The easiest way to implement the assignment operator is to use std::swap:
#include <algorithm>
//...
Vector& Vector::operator = (Vector v)
{
std::swap(v.datos, datos);
std::swap(v.n, n);
return *this;
}
This assumes you have a working, non-buggy copy constructor and destructor for Vector. See the copy / swap idiom for details on why this works.

Problem copying information into pointer array

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
};

How do I overload the << operator correctly in order to return an int value?

I can't seem to overload the << operator correctly. This is the code I have so far and the instructions for my assignment will be down below. If you point out other mistakes I made that would be kind, BUT my questions is how do I correctly overload my << operator in my case?
INTCOLLECTION.h:
#ifndef INTCOLLECTION_H
#define INTCOLLECTION_H
// Allocate memory in chunks of ints of this size.
const int CHUNK_SIZE = 5;
class IntCollection
{
private:
// The number of ints currently stored in the int
int size;
// the total number of elements available for storage
// in the data array
int capacity;
// A pointer to the dynamically allocated data array
int* data;
// a private member function to allocate more memory
// if necessary
void addCapacity();
public:
// Constructor
IntCollection();
// Destructor
~IntCollection();
// Copy constructor:
IntCollection(const IntCollection &c);
void add(int value);
int get(int index);
int getSize();
IntCollection& operator=(const IntCollection &c);
bool operator==(const IntCollection &c);
IntCollection& operator<<(int value);
};
#endif
INTCOLLECTION.cpp:
#include "IntCollection.h"
#include <iostream>
#include <cstdlib>
using namespace std;
IntCollection::IntCollection()
{
// Initialize member data to reflect an empty
// IntCollection
size = capacity = 0;
data = NULL;
}
IntCollection::~IntCollection()
{
delete [] data;
}
IntCollection::IntCollection(const IntCollection &c)
{
size = c.size;
capacity = c.capacity;
data = c.data;
for(int i = 0; i < c.size; i++)
{
data[i] = c.data[i];
}
}
void IntCollection::addCapacity()
{
// Create a new, bigger buffer, copy the current data to
// it, delete the old buffer, and point our data
// pointer to the new buffer
int *newData;
data = new int[capacity];
capacity += CHUNK_SIZE;
newData = new int[capacity];
for(int i = 0; i < size; i++)
{
newData[i] = data[i];
delete [] data;
data = newData;
}
}
void IntCollection::add(int value)
{
//first, allocate more memory if we need to
if(size == capacity)
{
addCapacity();
}
//Now, add the data to our array and increment size
data[size++] = value;
}
int IntCollection::get(int index)
{
if (index < 0 || index >= size)
{
cout << "ERROR: get() trying to access index out of range.\n";
exit(1);
}
return data[index];
}
int IntCollection::getSize()
{
return size;
}
IntCollection& IntCollection::operator=(const IntCollection &c)
{
size = c.size;
capacity = c.capacity;
data = c.data;
return *this;
}
bool IntCollection::operator==(const IntCollection &c)
{
if((size == c.size) && (capacity == c.capacity))
{
for(int m = 0; m < size; m++)
{
if(data[m] == c.data[m])
{
continue;
}
else
{
return false;
}
}
}
return true;
}
IntCollection& IntCollection::operator<<(int value)
{
return value; // <-- THIS IS WHERE I GET LOST!
/* I also tried changing the parameters to
(IntCollection &b, int value) to return b
but my teacher wants only one parameter
and it wasn't working that way either. */
}
INSTRUCTIONS:
For this assignment you will add a copy constructor, a destructor, and three overloaded operators to the IntCollection class. In the design diagram below, the black member functions represent code that has already been implemented. You will be implementing the green items. Each item that you will be adding to the class is described below the diagram.
Private:
int size // the number of ints currently stored in the int collection
int capacity // total number of elements available in data array
int* data // a pointer to the dynamically allocated data array
void addCapacity(); // private function to allocate more memory if necessary
Public:
IntCollection()
~IntCollection()
IntCollection(const IntCollection &c)
void add(int value)
int get(int index)
int getSize()
IntCollection& operator=(const IntCollection &c)
bool operator==(const IntCollection &c)
IntCollection& operator<<(int value)
The Copy Constructor. The copy constructor should perform a deep copy of the argument object, i.e. it should construct an IntCollection with the same size and capacity as the argument, with its own complete copy of the argument's data array.
The Assignment Operator (=). The assignment operator should also perform a deep copy of the argument object. It must return itself (or more efficiently, a reference to itself) in order to support multiple assignments on the same line, e.g. a = b = c. If you implement your assignment operator first it could be used in the copy constructor, but this is not a requirement.
The Is Equals operator (==). The "is equals" operator should return true if the argument object has the same size as the receiving object, and the values in both objects’ data arrays are identical.
The insertion operator (<<). The insertion operator should add the int parameter into the receiving IntCollection. The functionality is exactly the same as the add() function, i.e. add ints to the collection. Note, however, that this function must return a reference to itself in order to support multiple insertions on the same line, e.g. c << 45 << -210. Unlike the assignment operator, this return must be done by reference, because each insertion actually modifies the IntCollection object, and insertion is done from left to right.
The destructor. Function add() calls addCapacity() to allocate memory when it needs more room. Nowhere in this program is the memory deallocated with delete [], which means we have a memory leak! Add a destructor which correctly handles this.
addCapacity. Note that addCapacity() is a private member function. What happens if you try to call it from outside the class, i.e. by adding the line below to main()?
You need to return *this, i.e. the object being operated upon. Returning "by reference" has the same syntax as returning "by value"; the only difference is in the addition of & to the function declaration, which is already provided.
Try this:
IntCollection& IntCollection::operator<<(int value)
{
add(value);
return *this;
}

How to realloc in c++?

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;
}
}

How to return a dynamic object from operator function?

I am writing a operator function for - where my class object is a dynamic array of integer.
the operator takes lhs and rhs object and return an object which is the set of elements in lhs but not in rhs.
though I have written the function but I am not able to return the set since the destructor is called right after the object is returned.
IntegerSet & IntegerSet::operator - (IntegerSet & rhs) const
{
IntegerSet temp(capacity);//local object created to store the elements same size as lhs
int k=0;
int lhssize = ElementSize();//no. of elements in the set
int rhssize = rhs.ElementSize();
for (int i=0;i<lhssize;i++)
{
for (int j=0;j<rhssize;j++)
{
if (rhs.ptr[j]!=ptr[i])
{
k++;
}
}
if(k==rhssize)
{
temp = temp + ptr[i];
}
k=0;
}
return temp;
}
and here is the constructor if you cannot understand the object
IntegerSet::IntegerSet(const int & size)//works correctly
{
capacity = size;
ptr = new int [capacity]();
}
IntegerSet::IntegerSet(const int & size)//works correctly
{
capacity = size;
ptr = new int [capacity]();
}
IntegerSet::IntegerSet(const IntegerSet & copy) : capacity(copy.capacity)//works correctly
{
ptr = copy.clonemaker();
}
IntegerSet::~IntegerSet()
{
capacity = 0;
delete [] ptr;
}
int * IntegerSet::clonemaker() const // works correctly
{
if(ptr==NULL)
{
return NULL;
}
int *tempptr = new int [capacity];
for(int i=0;i<capacity;i++)
{
tempptr[i]=ptr[i];
}
return tempptr;
}
You'll have to return by value. The local object will be destroyed when the function returns, and there's no way to prevent that.
For that to work, your class will have to correctly follow the Rule of Three to make sure it's correctly copyable. In C++11 or later, you might also consider making it movable, to avoid unnecessary memory allocation and copying (although, in this case, the copy should be elided anyway).
Better still, follow the Rule of Zero and store a vector<int>, which will do all this for you, rather than trying to juggle raw pointers.
You need to change to return the result by value.
IntegerSet IntegerSet::operator - (IntegerSet & rhs) const
Also it would make more sense to supply rhs by const reference when taking a second look.