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.
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;
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;
}
}
I have a problem with sorting. I sort the objects containing the dynamic table. It seems that the stable_sort (or the vector) doesn't use a public copy constructor. I looks like they use a non-existent constructor with no parameter because the tables inside the objects are freed - I think.
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
class Dynamic{
int n;
int *tab;
public:
int getN() const{ return n;}
int *getTab() const {return tab;}
Dynamic(int ile){
n=ile;
tab=new int[n];
for(int i=0; i<n; i++)
tab[i] = (10-i)%10;
}
Dynamic(const Dynamic& d){
n = d.getN();
tab = new int[n];
for(int i=0; i<n; i++)
tab[i] = d.getTab()[i];
}
bool operator<(const Dynamic& a) const{
return n < a.getN();
}
~Dynamic(){
delete[] tab;
}
};
int test(vector<Dynamic> & c){
vector<Dynamic> d(c);
stable_sort(d.begin(), d.end());
d.clear();
}
int main(){
vector<Dynamic> c;
c.push_back(Dynamic(15));
test(c);
cout<<"test!";
return 0;
}
STL's sort is also affected but in slightly more complex way.
In g++-4.7.2 I can compile this and in running I get "double free or corruption (fasttop)"/core dumped (full report isn't helpful, I think). On online g++-4.9.0 it looks similar: "No output: Error: stdout maxBuffer exceeded.".
Where is my mistake? Thank you for your attention.
Well, you didn't overload the operator= for Dynamic, so the compiler implicitly defines one which would do bitwise copy. stable_sort() in your library calls the operator=, so tab in two Dynamic objects points to the same address, as a result, double delete on destruction. Overloading the operator= would resolve the problem:
Dynamic& operator =(const Dynamic& d)
{
// or use the copy-and-swap idiom
if(this != &d)
{
delete [] tab;
n = d.getN();
tab = new int[n];
for (int i = 0; i<n; i++)
tab[i] = d.getTab()[i];
}
return *this;
}
/This program is using template class. I don't know why but this program is throwing an error at run time related memory access violation. in below comments i will explain which line is causing this error./
#include<iostream>
using namespace std;
const int size =3;
template <class t="">
class vector
{
T* v;
public:
vector()
{
v=new T[size];
for(int i=0;i<size;i++)>
v[i]=0;
}
vector(T* a)
{
for(int i=0;i<size;i++)>
v[i]=a[i];
}
T operator*(vector &y)
{
T sum=0;
for(int i=0;i<size;i++)>
sum+=this->v[i] * y.v[i];
return sum;
}
void display(void)
{
for(int i=0;i<size;i++)>
{
cout<<v[i]<<"\t";
}
cout<<"\n";
}
};
int main()
{
int x[3]={1,2,3};
int y[3]={4,5,6};
vector<int> v1;
vector <int> v2;
v1=x; // This is causing an error
v2=y; // This is causing an error
//int R=v1*v2;
//cout<<"R = "<<R<<"\n";
cout<<"V1 = ";
v1.display();
cout<<"V2 = ";
v2.display();
cout<<"V1 x V2 = "<<v1*v2;
return 0;
}
It seems that the problem is in this constructor
vector(T* a)
{
for(int i=0;i<size;i++)>
v[i]=a[i];
}
You did not allocate memory for the array pointed by v.
2 Problems:
You are not initializing memory for your vector
You are calling the constructor incorrecly
To fix #1
vector(T* a)
{
v=new T[size];
for(int i=0;i<size;i++)>
v[i]=a[i];
}
To fix #2
int x[3]={1,2,3};
int y[3]={4,5,6};
vector<int> v1(x);
vector <int> v2(y);
the constructor vector(T * a) will get called and the member v is not initialized with anything. This causes undefined behavior. So to fix this you need to allocate in that constructor
vector(T* a)
{
v = new T[size];//this line is new
for(int i=0;i<size;i++)
v[i]=a[i];
}
My guess is there is no operator=(int []) defined for vector.
So in other words, compiler does not know what do you mean by v1=x when it comes to type int[] = vector<int>.
You are not calling the overloaded constructor there. So you either need to call it vector<int> v1(y); or implement vector::operator=(const T[] v);
I hope i got the types right
When calling the lines in question, the program is using a default assignment operator which is doing something that causes your violation. You need to implement this yourself, for example, this worked for me:
T& operator=(T const * a)
{
for(int i=0;i<size;i++)
v[i]=a[i];
return *this;
}