Deleting a member array of pointers, then reallocating it - c++

I'm creating a stack class as an exercise trying to learn some c++ concepts (initializer lists, memory management and templates here). I've run into something that I can't get my head around.
In function void Stack::push(const T& item), if I uncomment the delete [] data; line, my code runs well when the template argument is for example int or char. But with std::string, I get weird memory errors.
My thinking here is that I need a bigger array -> arrays can't be resized -> I create a new one -> I deallocate the memory I needed for the one that's soon to be not needed -> I make the existing pointer point to a new memory address where I create the bigger array.
Now, when I comment the delete line, the code runs well even with std::string, but I can't see why I can't do the delete operation safely with all types.
Any insights will be appreciated.
#include <iostream>
#include <stdio.h>
#include <memory.h>
template<class T>
class Stack
{
T* data;
int sz;
public:
//Stack(){sz=0;}
Stack(const std::initializer_list<T>&);
~Stack();
void push(const T&);
T& pop();
void show() const;
};
template<class T>
Stack<T>::Stack(const std::initializer_list<T> &list)
{
sz=0;
data = new T[list.size()];
for (auto i : list) {
data[sz] = i;
++sz;
}
std::cout<< "Created with sz: "<< sz<<std::endl;
}
template<class T>
Stack<T>::~Stack()
{
delete [] data;
}
template<class T>
void Stack<T>::push(const T& item) {
std::cout<<"push "<<item<<std::endl;
T* arr = new T[sz];
memcpy(arr, data, sz*sizeof(T));
//delete [] data;
data = new T[sz + 1];
memcpy(data, arr, sz*sizeof(T));
++sz;
data[sz - 1] = item;
std::cout<<"new size: "<<sz<<", bytes: "<<sz*sizeof(T)<<std::endl;
}
template<class T>
T& Stack<T>::pop()
{
if(sz > 0) {
std::cout<<"pop "<<data[sz-1]<<std::endl;
std::cout<<"new size: "<<sz-1<<std::endl;
return data[--sz];
}
else
return data[0];
}
template<class T>
void Stack<T>::show() const
{
for (int i=0; i<sz; i++) {
std::cout<<data[i]<<" ";
}
std::cout<<std::endl;
}
int main(){
Stack<int> s = {1,2,3,4,5,6,7,8,9,10,11};
s.show();
s.push(12);
s.push(13);
s.push(14);
s.pop();
s.pop();
s.push(15);
s.push(16);
s.show();
Stack<std::string> d = {"one","two","three"};
d.show();
d.pop();
d.push("four");
d.show();
return 0;
}

Don't use memcpy to copy objects, that will copy the bits alright, but for some object a bit-wise copy is not correct as the copy constructor (or copy assignment operator) Will not be used.
A good and simple example is if you have a stack of std::string objects. When you do a bit-wise copy (with memcpy) the contents of the std::string objects are copied, but that basically is just a pointer and a size. When you do a bit-wise copy then you will have two std::string objects referencing the same memory. Destroying one of those object will lead to the other having a stray pointer to some memory (that used to contain the string) that no longer is owned by your program.
To solve this use std::copy instead to copy the objects, it will do the right thing.
Unrelated to your problem, but your push function does a copy that it doesn't need:
T* arr = new T[sz];
memcpy(arr, data, sz*sizeof(T));
This is simply not needed, instead do something like
T* oldData = data;
data = new T[sz + 1];
// Copy from old array to new
std::copy(oldData, oldData + sz, data);
delete[] oldData;

Related

I can't understand how to solve problem with memory leak C++

Condition
In lectures, we have already started to implement our vector. In this task, you need to develop it: add the Size, Capacity, and PushBack methods. Send the simple_vector.h header file containing the SimpleVector class template declaration and definition for verification:
Requirements:
the Capacity method should return the current capacity of the vector — the number of elements that fit into the memory block currently allocated by the vector
the Size method must return the number of elements in the vector
the PushBack method adds a new element to the end of the vector; if there is no free space left in the current allocated memory block (i.e. Size() == Capacity()), the vector must allocate a block of size 2 * Capacity(), copy all the elements to it, and delete the old one.
the first call to the PushBack method for a newly created object must make the capacity equal to one
the Push Back method must have a amortized constant complexity
the begin and end methods must return iterators the current beginning and end of the vector
the current memory block allocated by the vector must be freed in the destructor
also see the attached solution template for additional requirements for working with SimpleVector in unit tests.
The preparation of the decision:
simple_vector.h: https://d3c33hcgiwev3.cloudfront.net/q-OL4qX_EeilzRLZf2WxfA_ac4e8270a5ff11e89fd0455a8819d387_simple_vector.h?Expires=1596067200&Signature=cLfBpytTripoqpOYaW9g4~2-JqTI~8HtxahNwNATwBeq28RdXCvkcqghN~UUPv~wx1XZTVOTs8JDsZQjEALk6Soy70QFADkK9lSfFpLNcQq-Dxd4oxk-C5QDEhadM1LrVGe8Rmz0jRYgIV5sDTvAATBhiY3k-KqbAaDe1AK6QiE_&Key-Pair-Id=APKAJLTNE6QMUY6HBC5A
simple_vector.cpp: https://d3c33hcgiwev3.cloudfront.net/uoPvEoauEeianAr0yIdmDg_bae6cec086ae11e88d9327752d64e780_simple_vector.cpp?Expires=1596067200&Signature=CE1Mox1yU6LjGDXL1xstxT9anv9NI~otNwhBw5AbPyLBquRIi9E6cotR~BQsrvU-djoksfjV9YgnsyF00eFnVjsk~oF0z18wkVkgdIirPB-NNLH0aFvD4WFG97qmSuD0WjeetWyi6UR5BKYCnwfO~ax6-HZLM-GWheO9LHc~BvE_&Key-Pair-Id=APKAJLTNE6QMUY6HBC5A
Comment:
The header file that you send for verification should not include the <vector>, <list>, <forward_list>, <deque>, <map> files. If you have one of these files enabled, you will get a compilation error.
Hint:
For sure, your implementation of the SimpleVector class template will have a field that is a pointer. In the default constructor, you will need to initialize it with something. In the lectures, we only discussed one way to initialize pointers — using the new operator. In C++, there is a special value that means a pointer that points to nothing — nullptr:
int* p = nullptr;
string* q = nullptr;
map<string, vector<int>>* r = nullptr;
You can use nullptr to initialize the pointer in the default constructor.
How to send:
When the work is ready, you can upload files for each part of the task on the 'My work'tab.
And here is my .hsolution to which the Coursera testing system responds a 10 != 8: Memory leak detected. However I can't figure out where the leak is going. Help me pls.
#pragma once
#include <cstdlib>
using namespace std;
template <typename T>
class SimpleVector {
public:
SimpleVector()
: data(nullptr)
, end_(data)
, size_(0) {}
explicit SimpleVector(size_t size)
: data(new T[size])
, end_(data + size)
, size_(size) {}
~SimpleVector() {
delete[] data;
}
T& operator[](size_t index) { return data[index]; }
T* begin() const { return data; }
T* end() const { return end_; }
size_t Capacity() const { return end_ - data; }
size_t Size() const { return size_; }
void PushBack(const T& value) {
if (size_ == Capacity()) {
if (size_ == 0) {
delete[] data;
data = new T[1];
data[size_] = value;
++size_;
end_ = data + size_;
}
else {
T* local_data = new T[size_];
for (size_t i = 0; i < size_; ++i) {
local_data[i] = data[i];
}
delete[] data;
data = new T[2 * Capacity()];
for (size_t i =0; i < size_; ++i) {
data[i] = local_data[i];
}
delete[] local_data;
data[size_] = value;
++size_;
end_ = data + size_ * 2;
}
}
else {
data[size_] = value;
size_++;
}
}
private:
T *data;
T *end_;
size_t size_;
};
Thank you in advance.
There is a memory leak in PushBack due to lack of exception safety. Consider:
T* local_data = new T[size_];
// potentially throwing operations here...
delete[] local_data;
If those operations throw, then delete[] local_data; will never be executed.
Typical way to avoid such memory leak is to use smart pointers instead of bare pointers for ownership. The antiquated way is to use try-catch.
Your class also fails to enforce the class invariant of uniqueness of data pointer. Such constraint is essential for the destructor to be correct, because an allocation must be deleted exactly once, and no more.
Making a copy of an instance of the class will result in undefined behaviour because of same pointer being deleted in multiple destructors. Another consequence is that the assigment operators will leak the previously allocated memory (before the UB occurs in the destructor):
{
SimpleVector vec(42);
SimpleVector another(1337);
SimpleVector vec = another; // memory leak in assignment operator
} // undefined behaviour in the destructor
The problem is in the copy and move constructors and assignment operators, which you've left as implicitly generated. The implicitly generated special member functions will copy the pointer value, violating its uniqueness (and failing to delete the previous allocation in case of assignment). In other words, those functions perform a shallow copy.
Using a smart pointer as the member is an easy solution. Otherwise, you must implement copy and move constructors and assignment operators that don't leak, nor violate uniqueness.
Note that even if you did use a smart pointer, you'd still need user defined copy etc. because of the end pointer. If you instead used an integer that is relative to data, then you could avoid defining those functions.
P.S. There is no need to allocate twice, and copy twice. Instead, allocate one larger buffer, copy the old one, delete the old, point to the new.
P.P.S. As a sidenote: The vector you are implementing behaves quite differently from the standard vector, which is probably intentional by your teacher. When I add an object to a vector of 10 elements, I would expect only one element to be created and possibly 10 be copied due to relocation, rather than 20 objects being created with 9 being unaccessible.
A proper implementation of vector separates the allocation of memory, and creation of objects into that memory which allows the growth of the memory to be geometric without creating objects until they are added into the vector. I suspect that how to do this is outside the scope of your exercise.
I wouldn't call it a leak, but you treat end_ inconsistently. It seems like you are treating Size and Capacity as equivalent values, they are not.
Either end_ should point one past the allocated (but not necessarily populated) memory, and you return data + size in end(), or it should point one past the last element, and you should store size_t capacity_ not size_t size_;
Here is solution without memory leak. Thank you.
#pragma once
#include <cstdlib>
using namespace std;
template <typename T>
class SimpleVector {
public:
SimpleVector() {
data_ = nullptr;
end_ = data_;
size_ = 0;
capacity_ = 0;
}
explicit SimpleVector(size_t size) {
data_ = new T[size];
end_ = data_ + size;
size_ = size;
capacity_ = size;
}
SimpleVector(const SimpleVector& that)
: data_(that.data_)
, end_(that.end_)
, size_(that.size_)
, capacity_(that.capacity_) {}
SimpleVector& operator = (const SimpleVector& that) {
data_ = that.data_;
end_ = that.end_;
size_ = that.size_;
capacity_ = that.capacity_;
}
~SimpleVector() { delete[] data_; }
T& operator[](size_t index) {
return data_[index];
}
T* begin() const { return data_; }
T* end() const { return data_ + size_; }
size_t Capacity() const { return capacity_; }
size_t Size() const { return size_; }
void PushBack(const T& value) {
if (size_ == capacity_) {
if (capacity_ == 0) { // т. е. создали конструктором по умолчанию, size_ = 0
data_ = new T[1];
capacity_ = 1;
data_[size_] = value;
++size_;
end_ = data_ + size_;
}
else if (capacity_ == size_) { // т. е. capacity_ == size_
T* local_data = new T[2 * size_];
for (size_t i = 0; i < size_; ++i) {
local_data[i] = data_[i];
}
delete[] data_;
data_ = new T[2 * size_];
for (size_t i = 0; i < size_; ++i) {
data_[i] = local_data[i];
}
delete[] local_data;
data_[size_] = value;
size_++;
capacity_ *= 2;
end_ = data_ + size_;
}
}
else {
data_[size_] = value;
size_++;
}
}
private:
T *data_;
T *end_;
size_t size_;
size_t capacity_;
};

End of array points to newly allocated array C++

Using C++, I am trying to create an array that holds pointers to objects I'm storing. But when the array is full, I want to expand the array.
the easy option is to allocate a new array with bigger size, then copy the elements to it, this is quite inefficient, and I thought of another way I want to try to do it:
create array of fixed size X
When full, create a new array, and make the end of the first array point to the start of the first element
Repeat as long as needed
What methods can I use to do that? I thought of one way to do it, but it seems very hacky:
declare all my new array as pointers to object pointer, then reinterprit_cast the filled elements to object pointer.
Note: I know I can use Vector, but I am instructed not to use std library.
Kind Regards,
There are some good answers in the comments already. I just want to provide a way to achieve exactly the behavior you described.
Since the elements of the array are pointers as well, you can define a union as the element of your array like this:
template<typename T>
union Cell
{
T* pElm;
Cell* pNext;//A fixed size array of Cells
}
And then build your array on top of it. For example:
template<typename T>
class SpecialArray
{
public:
//the next pointer is included
static const size_t ARRAY_LEN = 1000;// For example
using Pointer = T*;
using Segment = Cell<T>[ARRAY_LEN];
protected:
Segment* pFirst;
size_t mSize;
public:
SpecialArray()
:pFirst(nullptr),mSize(0){}
SpecialArray(SpecialArray&&){}
~SpecialArray(){}
Pointer& operator[](size_t index)
{
Segment* seg = pFirst;
size_t offest = 0;
//Search logic...
return seg[offest]->pElm;
}
const Pointer& operator[](size_t index) const;
};
Using C++, I am trying to create an array that holds pointers to
objects I'm storing. But when the array is full, I want to expand the
array.
With C++ templates and C primitives we can improvise a simple vector like below. And the grow buffer strategy is to double the size when the threshold is met.
#include <iostream>
#include <stdlib.h>
template <typename T>
class MyVector
{
public:
MyVector() : m_count(0), m_size(0), m_buffer(0)
{
m_size = bufInitSize;
m_buffer = (T*)malloc(sizeof(T) * bufInitSize);
}
~MyVector()
{
if (m_buffer)
free(m_buffer);
}
void add(const T& p)
{
if (m_count + 1 >= m_size)
{
m_size *= 2;
m_buffer = (T*)realloc(m_buffer, sizeof(T) * m_size);
}
m_buffer[m_count ++ ] = p;
}
T& operator[](int idx)
{
return m_buffer[idx];
}
private:
static const int bufInitSize = 1024;
T* m_buffer;
int m_count;
int m_size;
};
void main()
{
// using MyVector
MyVector<int*> vctOfIntPtr;
int n = 100;
vctOfIntPtr.add(&n);
int* pN = vctOfIntPtr[0];
std::cout << *pN;
}

Own vector error

I am trying create my own vector, I am at the beginning, and when compile e execute the code, i get "Program not responding". This is the code:
struct X
{
X(){};
~X(){};
int v1, v2, v3;
};
template<typename T>
class Vector
{
public:
// constructors
Vector();
Vector(unsigned s);
virtual ~Vector();
// overloaded operators
T operator[](unsigned index);
// others
void clear();
void add(T value);
unsigned getSize();
bool isEmpty();
private:
// pointer to first item of memory block
T* first;
unsigned size;
};
template<typename T>
Vector<T>::Vector()
{
first = NULL;
size = 0;
}
template<typename T>
Vector<T>::Vector(unsigned s)
{
size = s;
first = new T[s];
};
template<typename T>
Vector<T>::~Vector()
{
clear();
}
template<typename T>
void Vector<T>::clear()
{
for(unsigned i = size ; i > 0 ; i--)
delete &first[i];
first = NULL;
}
template<typename T>
void Vector<T>::add(T value)
{
T* temp = new T[size + 1]; // error happens here
// copy data to new location
for(unsigned i = 0 ; i < size ; i++)
temp[i] = first[i];
// delete older data
clear();
// add the new value in last index
temp[size + 1] = value;
// update the pointer
first = temp;
size++;
}
template<typename T>
T Vector<T>::operator[](unsigned index)
{
return first[index];
}
template<typename T>
unsigned Vector<T>::getSize()
{
return size;
}
template<typename T>
bool Vector<T>::isEmpty()
{
return first == NULL;
}
int main(int argc, char* args[])
{
Vector<X> anything;
X thing;
anything.add(thing);
anything.add(thing);
anything.add(thing); // if remove this line, program work fine.
}
As I commented, error happens in T* temp = new T[size + 1];.
If i define the value of v1, v2, v3 of X class, e.g. X() : v1(0), v2(0), v3(0) { }, the program works correctly.
If i change the type, e.g., Vector of int, he works perfectly.
If put X class in std::vector, work fine too.
Other comments are also accepted.
Can someone helpme?
Your description of the problem is incredibly vague, but I can point out problems with your code:
No vector copy constructor (causes double-deletes and crashes)
No vector copy assignment (causes double-deletes and crashes)
clear is incorrectly calling delete (causes crashes and corruption) (you should match your single new of an array with a single delete of the array. Don't loop over elements.
add is writing past the end of the array (causes crashes and corruption)
add is not exception safe
You have to fix at least the first four. The third and fourth are probably the causes of your hang.
You have a buffer overflow occurring.
T* temp = new T[size + 1]; // When size is 0, you allocate 1 space.
You then assign to the temp array, but in location temp[1], which isn't a valid location because your array has only 1 element. This is undefined behavior, and that this point, your program is free to continue however it chooses. In this case, it seems to loop indefinitely.
// add the new value in last index
temp[size + 1] = value; // When size is zero, your array is length '1', but
// you are accessing temp[1] which is outside the
// bounds of your allocated memory.

Dynamically alloceted array, double free or corruption

I understand that this error came from double deleting the allocated memory and in theory I know what should be done. But it seems to doesn't work the way it should. Or I do sth wrong. Please help.
Here's code of my class:
typedef int SIZE_TYPE;
template<typename T>
class CArrays{
private:
SIZE_TYPE size;
T * tab;
public:
// methods...
};
template<typename T>
CArrays<T>::CArrays(T value, SIZE_TYPE argsize){
size = argsize;
tab = new T[size];
for(SIZE_TYPE i = 0; i < size; i++)
*(tab+i) = value;
}
template<typename T>
CArrays<T>::~CArrays(){
delete [] tab;
}
template<typename T> template<typename J>
CArrays<T> & CArrays<T>::operator=(const CArrays<J> &rhs){
if(&rhs != this){
this->size = rhs.size;
delete [] this->tab;
this->tab = new T[this->size];
memcpy(this->tab, rhs.tab, sizeof(J) * rhs.size);
}
return *this;
}
And when i do in my main.cpp sth like that:
CArrays<int> a(3, 10), b(0, 10);
b = a;
*** glibc detected *** ./out: double free or corruption (fasttop): 0x000000000087b010 ***
There are several things wrong with your code:
Usage of memcpy to copy data.
If the template type T is a non-POD type, you cannot use memcpy to copy over the data. You have to copy the items one-by-one, or use std::copy
If you haven't coded one, you are missing the copy constructor.
Here is what it should look like:
template<typename T>
CArrays<T>::CArrays<T>(const CArrays<T> &rhs)
{
tab = new T[rhs.size];
for (size_t i = 0; i < rhs.size(); ++i )
tab[i] = rhs[i];
size = rhs.size;
}
The assignment operator is peculiar.
Maybe not wrong, but strange. An assignment operator would/should look like this (given that the copy constructor and destructor are working correctly):
#include <algorithm>
template<typename T>
CArrays<T>& CArrays<T>::operator=(const CArrays<T> &rhs)
{
CArrays<T> temp(rhs);
std::swap(temp.size, size);
std::swap(temp.tab, tab);
return *this;
}
The above works, due to copying and swapping out the internals of a temporary object with the existing objects, and letting the temporary die off.
In the last two items, I'm assuming that the only members are tab and size. If there are other members, please copy them also (and swap them out) in the code above.

Templates delete in C++ when List<List<X>>

I am trying to develop a generic List with templates. This list is compound by a Pointer array T* , an integer for getting the number of elements and some methods( find, contains...) It´s important to say that I cannot use std::library.
My problem comes when I am working with a List<List<int> > for instance.
One of the methods make a resizing of the T* pointer array, So when I have this List<List>>I create an auxpointer of a bigger size than T* and copying T content to auxpointer with a memcpy. The inner Pointers (list.T.T) are copied as pointers too, not memory duplicated, so when I delete the T* pointer and reasign T=auxpointer. I have already lost the data of that pointers in my new T.
template <typename T>
void CGenericList<T>::resize()
{
T* auxPointer = new T[this->maxElements*2];
memcpy (auxPointer,this->pointer,this->maxElements*sizeof(T));
delete[] this->pointer;
this->pointer=auxPointer;
this->maxElements=2*this->maxElements;
}
template<class T>
class CGenericList
{
public:
T* pointer;
int N;
int maxElements;
CGenericList();
CGenericList(int);
~CGenericList();
void resize();
}
Can anyone give me any tips for doing it?
The code you posted shows some problems.
T* auxPointer = new T[this->maxElements*2];
here you allocate a new array of maxElements*2 - and call the default constructor.
Which in your case probably initialises all Listelements.
memcpy (auxPointer,this->pointer,this->maxElements*sizeof(T));
After that you copy the content of your old array to the memory area of the newly allocated memory. This overwrites the pointers to the just created Listelements with the ones from the old array -> memory leak.
delete[] this->pointer;
Then you delete the array, this calls the destructors of all elements.
Which hopefully will delete their content and free their memory.
this->pointer=auxPointer;
Finally you reassign the newly created array. The pointers in the list point to the old listselements and point to not allocated memory anymore (because of the call to the destructor via delete[]).
A solution would be to implement an copy constructor for your list and call it for all
elements in your array. (DeepCopy) And of course an assignment operator, i almost forgot ;)
CGenericList(const CGenericList<T>& copy);
CGenericList<T>& operator= (const CGenericList<T>& rhs)
Probably somethig like this - be aware that this is "asis" and definitely not exceptionsafe ;)
template<class T>
class CGenericList
{
public:
T* pointer;
int N;
int maxElements;
CGenericList();
CGenericList( const CGenericList<T>& copy );
CGenericList<T>& operator=(const CGenericList<T>& rhs);
CGenericList(int);
~CGenericList();
void resize();
};
template <typename T>
void CGenericList<T>::resize()
{
T* auxPointer = new T[this->maxElements*2];
for(int i=0; i < this->maxElements; i++)
{
auxPointer[i] = this->pointer[i];
}
delete[] this->pointer;
this->pointer = auxPointer;
this->maxElements = this->maxElements*2;
}
template <typename T>
CGenericList<T>::CGenericList()
:N(0)
,maxElements(0)
{
this->pointer = new T[1];
}
template <typename T>
CGenericList<T>::CGenericList(const CGenericList<T>& copy)
:N(copy.N)
,maxElements(copy.maxElements)
{
T* temp = new T[copy.maxElements];
for(int i=0; i<N; i++ )
{
temp[i] = copy.pointer[i];
}
this->pointer = temp;
}
template <typename T>
CGenericList<T>& CGenericList<T>::operator=(const CGenericList<T>& rhs)
{
if( this != &rhs )
{
delete[] this->pointer;
this->pointer = new T[rhs.maxElements];
for(int i=0; i<rhs.maxElements; i++)
{
this->pointer[i] = rhs.pointer[i];
}
}
return *this;
}
template <typename T>
CGenericList<T>::CGenericList(int size)
:N(0)
,maxElements(size)
{
this->pointer = new T[size];
}
template <typename T>
CGenericList<T>::~CGenericList()
{
delete[] this->pointer;
}
int main(int /*argc*/, char */*argv*/[])
{
CGenericList<CGenericList<int> > list;
list.resize();
return 0;
}
If you don't like to use stl you can have a look at stlport
Your resize is not exception safe. You are first deleting the existing array and then allocating memory for a different size and then assigning auxPointer.
Coming to the problem you are having, check if the below approach helps.
T* auxPointer = new T[this->maxElements*2];
for ( int i =0; i < this->maxElements; ++i)
std::swap(auxPointer[i], pointer[i]);
delete[] this->pointer;
this->pointer = auxPointer;
this->maxElements=2*this->maxElements;