I'm trying to implement a dynamic array in c++. once it becomes full, its size is doubled and is copied to the double-sized array.
it looks fine but Valgrind says I have a leak.
I should say that I'm not allowed to use any of c++ STL structures- that's why I'm using new[] and delete[] operators.
this is my code:
template<class T>
class DynamicArray
{
int size;
int numOfElements;
T* arr;
public:
DynamicArray(int size) :
size(size), numOfElements(0)
{
arr = new T[size];
}
;
int getSize()
{
return size;
}
int getNumberOfElements()
{
return numOfElements;
}
void insert(T element)
{
arr[numOfElements++] = element;
if (numOfElements == size)
{
T* extended_array = new T[size * 2];
for (int i = 0; i < numOfElements; i++)
{
extended_array[i] = arr[i];
}
delete[] arr;
arr = extended_array;
size = size * 2;
}
}
T& operator[](int i)
{
if (!((i >= 0) && (i < size)))
throw arrayOutOfBoundsException();
return arr[i];
}
~DynamicArray()
{
delete[] arr;
}
class arrayOutOfBoundsException: std::exception
{
};
};
My main program is:
using std::cout;
using std::endl;
void printIntArray(DynamicArray<int> arr){
cout << "size: " << arr.getSize() << "; " << "numOfElemnts: " << arr.getNumberOfElements() << endl;
cout << "array: ";
for(int i=0; i<arr.getNumberOfElements(); i++){
cout << " " << arr[i];
}
cout << endl << endl;
}
int main() {
DynamicArray<int> arr(5);
printIntArray(arr);
arr.insert(1);
arr.insert(2);
arr.insert(3);
printIntArray(arr);
arr.insert(4);
printIntArray(arr);
arr.insert(5);
printIntArray(arr);
arr.insert(6);
printIntArray(arr);
arr.insert(7);
arr.insert(8);
arr.insert(9);
printIntArray(arr);
arr.insert(16);
printIntArray(arr);
arr[9] = 901;
arr[0] = 101;
printIntArray(arr);
**valgrind shows me this error- when the destructor is compiled as comment:
==3954== 80 bytes in 1 blocks are definitely lost in loss record 1 of 1
==3954== at 0x4A07192: operator new[](unsigned long) (vg_replace_malloc.c:363)
==3954== by 0x40103E: DynamicArray<int>::insert(int) (DynamicArray.h:41)
==3954== by 0x400DBC: main (main.cpp:44)**
Can anybody tell me what I was doing wrong? am I misusing "delete[]"? or the destructor?
thanks
The behaviour described by the asker is impossible or
when the destructor is compiled as comment:
means the destructor has been commented out and this is causing the leak. This has been confirmed in a comment. Without the destuctor the last allocation will never be put away.
The Asker's real problem is a Rule of Three Violation.
The short version goes something like this:
If a class requires a user defined copy constructor, assignment operator, or destructor it almost always requires all three.
More on that here: What is The Rule of Three?
In this case,
void printIntArray(DynamicArray<int> arr)
accepts arr by value. This means arr is a copy of the value passed in. In the absence of a custom copy constructor the arr inside printIntArray will point to the same allocation as the source. The copy will be destroyed at the end of printIntArray, and the destructor will delete the allocation, leaving the source pointing to invalid memory so...
DynamicArray<int> arr(5); // allocated storage inside arr
printIntArray(arr); // made a copy of arr that points to same storage.
// Storage freed when copy destroyed
arr.insert(1); // invokes undefined behaviour writing into invalid memory
arr.insert(2); // same
arr.insert(3); // same
printIntArray(arr); // copies arr again. This copy is pointing to the same invalid memory
// and will try to free the allocation a second time when the
// copy is destroyed
The program will almost certainly crash here, making it impossible to leak from the users code. But because the program crashed, Crom only knows what behind-the-scenes structures the runtime was using were not correctly put away.
Whatever happened, as of
arr.insert(1);
the program was deep in Undefined Behaviour and whatever happens after that is anybody's guess. Memory leaks are the least of your worries.
Fixing: Satisfy the Rule of Three
Implement a copy constructor and an assignment operator (We'll ignore the Rule of Five for now.)
You could change
void printIntArray(DynamicArray<int> arr)
to pass by reference
void printIntArray(DynamicArray<int> & arr)
and then
DynamicArray(const DynamicArray &) = delete;
DynamicArray& operator=(const DynamicArray &) = delete;
and disable all copying, but this seems a bit draconian.
Instead lets use Copy and Swap because it's simple and easy to understand. Note that it can also be inefficient, so it's not always the right tool. Here it fits what we want.
DynamicArray(const DynamicArray & src) :
size(src.size), numOfElements(src.numOfElements), arr(new T[size])
{
for (int i = 0; i < numOfElements; i++)
{
arr[i] = src.arr[i];
}
}
DynamicArray& operator=(const DynamicArray src)
{
std::swap(size, src.size);
std::swap(numOfElements, src.numOfElements);
std::swap(arr, src.arr);
return *this;
}
And because who wants to copy they don't need to we'll still
void printIntArray(DynamicArray<int> & arr)
as soon as we're done testing that the copy constructor works correctly.
Related
I am learning C++ and I am running in a problem that I don't know the reason for.
I have created a vector and matrix class that are light wrappers around a C array that manage a T* array on the heap.
The Matrix class has a function:
// in NBDMatrix.hpp class definition
void makeXMeshgridv1(NBDVector<T> v){ // takes a value
for(unsigned int j=0; j<ny; j++){
for(unsigned int i=0; i<nx; i++)
this->at(i,j) = v.at(i);
}
}
void makeXMeshgridv2(NBDVector<T>& v){ // takes a reference
for(unsigned int j=0; j<ny; j++){
for(unsigned int i=0; i<nx; i++)
this->at(i,j) = v.at(i);
}
}
in main()
NBDVector<float> vec1 = NBDVector<float>(0.0f, 12.6f, 4);
NBDVector<float> vec2 = NBDVector<float>(0.0f, 12.6f, 4);
NBDMatrix<float> mat1(4,8);
NBDMatrix<float> mat2(4,8);
mat1.makeXMeshgridv1(vec1); // causes problem at destruction
mat2.makeXMeshgridv2(vec2); // no problem at destruction
If I use makeXMeshgridv1() I get
malloc: *** error for object 0x100604c50: pointer being freed was not allocated at destruction
but when I use makeXMeshgridv2 everything goes well.
I would like to understand what is the problem when using makeXMeshgridv1().
The one passed by value is likely not being properly deleted when it leaves its scope.
As others have mentioned you need to follow the rule of 0/3/5. The rule of 5 is for optimization so, you can focus on the rule of 0 and 3 for now.
https://en.cppreference.com/w/cpp/language/rule_of_three
This is the example at the page linked.
rule_of_three(const char* s = "")
: rule_of_three(s, std::strlen(s) + 1)
{}
~rule_of_three()
{
delete[] cstring; // deallocate
}
rule_of_three(const rule_of_three& other) // copy constructor
: rule_of_three(other.cstring)
{}
rule_of_three& operator=(rule_of_three other) // copy assignment
{
std::swap(cstring, other.cstring);
return *this;
}
I'm passing an array to a constructor. Constructor has two parameters, pointer to int called data and int which is size of an array.
I'm allocation dynamic memory in constructor definition for this array and passing array pointer to this storage.
Last but one step is printing values in array through the pointer which is pointing to the first int value of this array.
The last step is freeing up memory in destructor delete [] data. In this step I got an error message: Debug Assertion Failed! Expression: _CrtlsValidHeapPpinter(block).
I'm very new in C++ so I'm struggling what I did wrong in below program. Could you give me a hint, please?
#include <iostream>
class Test
{
private:
int* data;
int size;
public:
// constructor and destructor
Test(int* d, int s);
~Test();
// few void methods
void display_data(int size)
{
for (int i{ 0 }; i < size; ++i)
{
std::cout << data[i] << " ";
}
std::cout << std::endl;
}
};
Test::Test(int* d, int s)
: data{nullptr}, size(s)
{
data = new int[s];
data = d;
}
Test::~Test()
{
std::cout << "Destructor is freeing memory" << std::endl;
delete[] data;
}
int main()
{
int data_array[5]{ 2,8,6,10,20 };
Test* t1 = new Test(data_array, 5);
t1->display_data(5);
delete t1;
return 0;
}
In yout Test::Test constructor you don't copy the array, you just copy the pointer. You need to use std::copy or memcpy (C-style) to copy the contents of d into data.
However, I would recommend to use STL containers (i.e., std::vector) instead of raw pointers. It will allow you to get rid of manual resource management (new/delete), which is error-prone and redundant.
You need to copy the data here. Instead of :
data = new int[s];
data = d;
Which creates an array and then forgets about it. This would lead to the array being deleted multiple times!
Copy the content of your array:
std::copy(d, d+s, data);
Or even better, use std::vector.
I'm working through the Stroustrup C++ 11 book, and I ran into a double free exception. I understand that it's freeing the memory twice, but what I don't understand is why it's happening for a function that's passing by copy:
#include <iostream>
using namespace std;
namespace ALL_Vector {
class Vector {
public:
// Intitialize elem and sz before the actual function
Vector(int size) :elem {new double[size]}, sz {size} {};
~Vector() {delete[] elem;};
double& operator[](int i) {
return elem[i];
};
int size() {return sz;};
private:
double* elem;
int sz;
};
void print_product(Vector& y) {
double result {1};
for (auto x = 0; x < y.size() ; x++){
if (y[x] > 0) {result *= y[x]; };
}
cout << "The product of Vector y is: " << result << ", or so it would appear ;)\n";
}
}
/*
Self test of the Vector class.
*/
int main(){
ALL_Vector::Vector myVector(15);
cout << "The size of Vector y is: " << myVector.size() << "\n";
myVector[0] = 12;
myVector[2] = 7;
myVector[3] = 19;
myVector[4] = 2;
ALL_Vector::print_product(myVector);
return 0;
}
print_product() is taking the Vector class and creating a new Vector with duplicated contents? Why would this cause a double free? I'm assuming that RIIA in this instance is somehow interacting with the Vector::~Vector(), something like a race condition?
I know if I change this to pass it's argument by reference it will avoid the double free. I'm trying to better understand the issue with passing by copy.
Thanks!
Actually you are calling print_product with a reference to myVector, so everything is fine.
Troubles begin with passing myVector by value because default copy constructor will copy elem pointer instead of duplicating the whole array.
Both ALL_Vector::Vector elem pointer will refer to same memory storage and thus be deleted twice.
To tackle this issue, you have to implement copy constructor to create a new array and copy all elements.
If you pass Vector per value, the copy constructor is called, not the constructor you have implemented.
In this case, elem is not duplicated but the pointer is copied in the new object and then deleted twice by the destructor.
You have to implement a copy constructor that allocate a new elem and copy all elements.
Suppose we have the following:
class StringClass
{
public:
...
void someProcessing( );
...
StringClass& operator=(const StringClass& rtSide);
...
private:
char *a;//Dynamic array for characters in the string
int capacity;//size of dynamic array a
int length;//Number of characters in a
};
StringClass& StringClass::operator=(const StringClass& rtSide)
{
capacity = rtSide.capacity;
length = rtSide.length;
delete [] a;
a = new char[capacity];
for (int i = 0; i < length; i++)
a[i] = rtSide.a[i];
return *this;
}
My question is: why does this implementation of overloading the assignment operator cause problems when we try to assign an object to itself like:
StringClass s;
s = s;
The textbook I'm reading (Absolute C++) says that after delete [] a; "The pointer s.a is then undefined. The assignment operator has corrupted the object s and this run of the program is probably ruined."
Why has the operator corrupted s? If we're reinitalizing s.a right after we delete it, why does this cause such a problem in the program that we have to redefine the function as:
StringClass& StringClass::operator=(const StringClass& rtSide)
{
if (this == &rtSide)
//if the right side is the same as the left side
{
return *this;
}
else
{
capacity = rtSide.capacity;
length = rtSide.length;
delete [] a;
a = new char[capacity];
for (int i = 0; i < length; i++)
a[i] = rtSide.a[i];
return *this;
}
}
If you are assigning an object to itself both a and rt.a point to the same string, so when you do delete [] a you are deleting both what a and rt.a point to; then you do reallocate it, but the data you were going to copy (on itself) in the loop has been lost in the delete.
In the loop now you will just copy whatever junk happens to be in the memory returned by new on itself.
By the way, even with the "safety net" of the self-assignment check that assignment operator isn't completely ok (for instance, it's not exception safe); the "safe" way to define the "big three" (copy constructor, assignment operator, destructor) is using the "copy and swap idiom".
If you self-assign, you free (delete) the string via the LHS argument before you copy it to the newly allocated space via the RHS argument. This is not a recipe for happiness; it is undefined behaviour and anything may happen. A crash is plausible; if you're really unlucky, it may appear to work.
Consider what the value of rtSide.a is when you're inside the broken operator=.
It's the same as this->a, the values you just clobbered. Accessing non-owned memory is undefined behavior, thus accessing this->a is undefined behavior (since you just freed it).
delete [] a;
a = new char[capacity];
for (int i = 0; i < length; i++)
a[i] = rtSide.a[i]; //Invalid when this->a == rtSide.a
//because rtSide.a is no longer owned by your program.
If you did actually want to do this, you would have to make a copy of a before deleting it:
char* ca;
if (this == &rtSide) {
ca = copy of rtSide.a or this->a;
} else {
ca = rtSide.a;
}
//Do your assigning and whatnot
if (this == &rtSide) {
delete[] ca;
}
Obviously it's much more efficient to just do nothing instead of making temporary copies of all of an instances own members. It's the same concept as doing int x = 5; int y = x; x = y;
It is because you've first deleted the pointer delete [] a;
and then later on trying to copy from the deleted location:
for (int i = 0; i < length; i++)
a[i] = rtSide.a[i]; //rtSide has already been deleted as 'this' and '&rtSide' are same.
Remember it is the same location you are trying to copy from, which you've already deleted.
Hence, the error!
The later code you posted fixes this problem by checking for self-assignment as a separate case.
delete [] a;
a = new char[capacity];
for (int i = 0; i < length; i++)
a[i] = rtSide.a[i];
That's why. Think of it like this:
You delete whatever a points to, then allocate a new chunk of memory. The new chunk of memory contains garbage which becomes your new data. Do not be confused by the loop that does a[i] = rtSide.a[i]; that only copies the garbage onto itself.
Remember, this and rtSide both lead you to the same object. When you modify the object using this the object that rtSide refers to is modified.
Consider the program below. It has been simplified from a complex case. It fails on deleting the previous allocated memory, unless I remove the virtual destructor in the Obj class. I don't understand why the two addresses from the output of the program differ, only if the virtual destructor is present.
// GCC 4.4
#include <iostream>
using namespace std;
class Arena {
public:
void* alloc(size_t s) {
char* p = new char[s];
cout << "Allocated memory address starts at: " << (void*)p << '\n';
return p;
}
void free(void* p) {
cout << "The memory to be deallocated starts at: " << p << '\n';
delete [] static_cast<char*> (p); // the program fails here
}
};
struct Obj {
void* operator new[](size_t s, Arena& a) {
return a.alloc(s);
}
virtual ~Obj() {} // if I remove this everything works as expected
void destroy(size_t n, Arena* a) {
for (size_t i = 0; i < n; i++)
this[n - i - 1].~Obj();
if (a)
a->free(this);
}
};
int main(int argc, char** argv) {
Arena a;
Obj* p = new(a) Obj[5]();
p->destroy(5, &a);
return 0;
}
This is the output of the program in my implementation when the virtual destructor is present:
Allocated memory address starts at: 0x8895008
The memory to be deallocated starts at: 0x889500c
RUN FAILED (exit value 1)
Please don't ask what the program it's supposed to do. As I said it comes from a more complex case where Arena is an interface for various types of memory. In this example the memory is just allocated and deallocated from the heap.
this is not the pointer returned by the new at line char* p = new char[s]; You can see that the size s there is bigger than 5 Obj instances. The difference (which should be sizeof (std::size_t)) is in additional memory, containing the length of the array, 5, immediately before the address contained in this.
OK, the spec makes it clear:
http://sourcery.mentor.com/public/cxx-abi/abi.html#array-cookies
2.7 Array Operator new Cookies
When operator new is used to create a new array, a cookie is usually stored to remember the allocated length (number of array elements) so that it can be deallocated correctly.
Specifically:
No cookie is required if the array element type T has a trivial destructor (12.4 [class.dtor]) and the usual (array) deallocation function (3.7.3.2 [basic.stc.dynamic.deallocation]) function does not take two arguments.
So, the virtual-ness of the destructor is irrelevant, what matters is that the destructor is non-trivial, which you can easily check, by deleting the keyword virtual in front of the destructor and observe the program crashing.
Based on chills' answer, if you want to make it "safe":
#include <type_traits>
a->free(this - (std::has_trivial_destructor<Obj>::value ? 1 : 0));