patterncppMinor
Dynamic resizeable array
Viewed 0 times
arrayresizeabledynamic
Problem
How can I improve this code for a dynamic and re-sizable array data structure?
Array.h
Array.cpp
```
#include "Array.h"
#include
#pragma region Private Methods
void Array :: SetSize(int size)
{
this->m_Size = size;
}
void Array :: AllocateMemoryOfSize(int size)
{
this->m_ArrayContainer = new int[size];
this->SetSize(size);
}
void Array :: DeallocateMemory()
{
if(this->GetSize() > 0)
{
delete [] this->m_ArrayContainer;
this->SetSize(0);
}
}
void Array :: SetItem(int index, int value)
{
this->m_ArrayContainer[index] = value;
}
#pragma endregion
Array :: Array()
{
//std::coutm_ArrayContainer = NULL;
this->SetSize(0);
}
Array :: Array(int size)
{
//std::coutSetSize(0);
this->Resize(size);
}
void Array :: Resize(int newSize)
{
int oldSize = this->GetSize();
for(int i=0 ; iSetItem(i, NULL);
}
this->DeallocateMemory();
this->AllocateMemoryOfSize(newSize);
this->SetSize(newSize);
}
Array :: Array(Array & arr)
{
//std::coutSetSize(0);
int size = arr.GetSize();
this->Resize(size);
for(int i=0 ; iSetItem(i, arr.GetItem(i));
}
}
void Array :: operator = (Array & arr)
{
//std::coutSetSize(0);
int size = arr.GetSize();
this->Resize(size);
for(int i=0 ; iSetItem(i, arr.GetItem(i));
}
}
int Array :: GetSize()
{
return this->m_Size;
}
int Array :: GetItem(int index)
{
r
Array.h
#ifndef _ARRAY_H_
#define _ARRAY_H_
class Array
{
private:
int * m_ArrayContainer;//These two variables
int m_Size; //work in pairs.
private:
void SetSize(int size);
void AllocateMemoryOfSize(int size);
void DeallocateMemory();
public:
Array();
Array(int size);
Array(Array & arr);
void operator = (Array & arr);
void Resize(int newSize);
int GetSize();
void SetItem(int index, int value);
int GetItem(int index);
~Array();
};
#endifArray.cpp
```
#include "Array.h"
#include
#pragma region Private Methods
void Array :: SetSize(int size)
{
this->m_Size = size;
}
void Array :: AllocateMemoryOfSize(int size)
{
this->m_ArrayContainer = new int[size];
this->SetSize(size);
}
void Array :: DeallocateMemory()
{
if(this->GetSize() > 0)
{
delete [] this->m_ArrayContainer;
this->SetSize(0);
}
}
void Array :: SetItem(int index, int value)
{
this->m_ArrayContainer[index] = value;
}
#pragma endregion
Array :: Array()
{
//std::coutm_ArrayContainer = NULL;
this->SetSize(0);
}
Array :: Array(int size)
{
//std::coutSetSize(0);
this->Resize(size);
}
void Array :: Resize(int newSize)
{
int oldSize = this->GetSize();
for(int i=0 ; iSetItem(i, NULL);
}
this->DeallocateMemory();
this->AllocateMemoryOfSize(newSize);
this->SetSize(newSize);
}
Array :: Array(Array & arr)
{
//std::coutSetSize(0);
int size = arr.GetSize();
this->Resize(size);
for(int i=0 ; iSetItem(i, arr.GetItem(i));
}
}
void Array :: operator = (Array & arr)
{
//std::coutSetSize(0);
int size = arr.GetSize();
this->Resize(size);
for(int i=0 ; iSetItem(i, arr.GetItem(i));
}
}
int Array :: GetSize()
{
return this->m_Size;
}
int Array :: GetItem(int index)
{
r
Solution
You have a couple of memory leaks.
First, the
However, in C++ it's legal to allocate a memory block with 0 size. (See the Q&A C++ new int[0] -- will it allocate memory? on Stack Overflow for more information.) When you allocate memory, you actually get two things: the memory that you asked for, and some data that the C++ runtime uses to keep track of that memory block.
If you have a sequence of code like:
the constructor will allocate a 0-sized block of memory and the extra data for the C++ runtime. When
Second, in your assignment operator, the first thing you do is to set the size of
Next, you call
Note that the
Then you call
To fix these bugs, I would:
-
Check if
-
Don't zero out the memory in
-
Don't set the size field independently of the allocation or deallocation of memory. You have calls to
In the context of your assignment operator, this becomes:
Note that there are still other ways that this class could fail, for example if an exception is thrown during allocation. I recommend reading about exception guarantees: in a nutshell, do the memory allocation and copying of the other instance into temporary memory first, then if that succeeds, free up the resources in this instance and assign the temporary values to them. Scott Meyers covers it well in his book, Effective C++.
Speaking of the assignment operator, it's customary to have it return a reference to
This will allow you to chain together assignments like:
First, the
DeallocateMemory() will only delete the object's memory block if the size is greater than 0:void Array :: DeallocateMemory()
{
if(this->GetSize() > 0) // GetSize() will return zero at this point
{
delete [] this->m_ArrayContainer;
this->SetSize(0);
}
}However, in C++ it's legal to allocate a memory block with 0 size. (See the Q&A C++ new int[0] -- will it allocate memory? on Stack Overflow for more information.) When you allocate memory, you actually get two things: the memory that you asked for, and some data that the C++ runtime uses to keep track of that memory block.
If you have a sequence of code like:
Array a(0);
a.Resize(1);the constructor will allocate a 0-sized block of memory and the extra data for the C++ runtime. When
Resize() calls DeallocateMemory(), it'll see that your data block is 0-sized, won't invoke delete[], and leak that memory when AllocateMemoryOfSize() overwrites the object's m_ArrayContainer pointer:void Array :: AllocateMemoryOfSize(int size)
{
this->m_ArrayContainer = new int[size]; // Memory leak!
this->SetSize(size);
}Second, in your assignment operator, the first thing you do is to set the size of
this to zero:void Array :: operator = (Array & arr)
{
this->SetSize(0);
int size = arr.GetSize();
this->Resize(size);
/// [stuff elided].
}Next, you call
Resize with the size of the other object:void Array :: Resize(int newSize)
{
int oldSize = this->GetSize();
for(int i=0 ; iSetItem(i, NULL);
}
this->DeallocateMemory();
this->AllocateMemoryOfSize(newSize);
this->SetSize(newSize);
}Note that the
for-loop at the start doesn't actually do anything because the size of this has been set to zero by the time you get here. Even if it didn't, it would be unnecessary because the intent is to delete[] that memory soon afterwards, and once it has been returned to the heap, the contents of memory are irrelevant.Then you call
DeallocateMemory(), which won't delete any memory because the size field in this has already been set to zero. Finally, you call AllocateMemoryOfSize() which unconditionally overwrites the pointer to the array, leaking memory the same way as in my first point above.To fix these bugs, I would:
-
Check if
m_ArrayContainer is a null pointer to decide if I need to delete[] it.void Array :: DeallocateMemory()
{
if(this->m_ArrayContainer != NULL)
{
delete [] this->m_ArrayContainer;
this->m_ArrayContainer = NULL;
this->SetSize(0);
}
}-
Don't zero out the memory in
this just before deleting it; your Resize() method then becomes:void Array :: Resize(int newSize)
{
this->DeallocateMemory();
this->AllocateMemoryOfSize(newSize);
this->SetSize(newSize);
}-
Don't set the size field independently of the allocation or deallocation of memory. You have calls to
SetSize() in several places in your code: remove all except the ones in AllocateMemoryOfSize() and DeallocateMemory().In the context of your assignment operator, this becomes:
void Array :: operator = (Array & arr)
{
int size = arr.GetSize();
this->Resize(size);
for(int i=0 ; iSetItem(i, arr.GetItem(i));
}
}Note that there are still other ways that this class could fail, for example if an exception is thrown during allocation. I recommend reading about exception guarantees: in a nutshell, do the memory allocation and copying of the other instance into temporary memory first, then if that succeeds, free up the resources in this instance and assign the temporary values to them. Scott Meyers covers it well in his book, Effective C++.
Speaking of the assignment operator, it's customary to have it return a reference to
this:Array &Array::operator= (Array & arr)
{
// [rest of the assignment operator elided]
return *this;
}This will allow you to chain together assignments like:
Array a, b, c;
// ... stuff ...
a = b = c;Code Snippets
void Array :: DeallocateMemory()
{
if(this->GetSize() > 0) // GetSize() will return zero at this point
{
delete [] this->m_ArrayContainer;
this->SetSize(0);
}
}Array a(0);
a.Resize(1);void Array :: AllocateMemoryOfSize(int size)
{
this->m_ArrayContainer = new int[size]; // Memory leak!
this->SetSize(size);
}void Array :: operator = (Array & arr)
{
this->SetSize(0);
int size = arr.GetSize();
this->Resize(size);
/// [stuff elided].
}void Array :: Resize(int newSize)
{
int oldSize = this->GetSize();
for(int i=0 ; i<oldSize ; i++)
{
this->SetItem(i, NULL);
}
this->DeallocateMemory();
this->AllocateMemoryOfSize(newSize);
this->SetSize(newSize);
}Context
StackExchange Code Review Q#94935, answer score: 4
Revisions (0)
No revisions yet.