My Dynamic Array template class is doing weird things - c++

The code
I'm having a problem with it handling strings. Whenever I try a string, I get a lovely little error code:
Unhandled exception at 0x77e815de in arraytest.exe: 0xC0000005:
Access violation reading location 0xabababab.
It handles int, double, and char just fine. I also tried the exact same test string I was using (the alphabet) as a char* and it did that fine too. It just pukes on strings, for a reason that I cannot quite fathom. I really don't know where to go with it next. Any suggestions?

You have a problem with your reallocation code in push_back
template<class type>
void DynamicArray<type>::push_back(type newValue)
{
++_size; // <=== **
if (_size > _capacity) // If size larger than capacity
{
_capacity *= CAPACITY_MULT; // double capacity
type* tempArray = new type[_capacity];// Create temporary array with new capacity
// Copy array values into temporary array
for (int i = 0; i < _size; i++) // <=== **
{
tempArray[i] = dynArray[i];
}
// Delete dynArray
delete[] dynArray;
// Insert new value at end of temporary array
// Set dynArray to temporary array
dynArray = tempArray;
}
dynArray[_size - 1] = newValue;
}
You start by increasing the size of the current container before checking if there is room for the new element. Then you use the new size when copying the old values to a new array.
This might seem to work for primitive types, but a std::string will try to copy its own internal data (which isn't there) and liklely cause an access violation.

Related

Leaking memory when not freeing internal cells?

My professor wrote the following code:
template <class T>
Set<T>& Set<T>::operator=(const Set<T>& set) {
if (this == &set) return *this;
T* data_temp = new T[set.size];
try {
for (int i = 0; i < size; ++i) {
temp_data[i] = set.data[i];
}
} catch (...) {
delete[] temp_data;
throw;
}
delete[] data;
data = temp_data;
size = maxSize = set.size;
return *this;
}
And he pointed that temp_data[i] = set.data[I]; calls operator=, and I am wondering why this doesn't leak memory?
For example if operator= failed in the 4th loop then we are deleting temp_data, but what about the values of the first 3 cells in temp_data which were allocated inside operator= code? we aren't freeing them.
For example if operator= failed in the 4th loop then we are deleting temp_data, but what about the values of the first 3 cells in temp_data which were allocated inside operator= code? we aren't freeing them.
new[] allocates the entire array and constructs all of the T objects in it, before the loop is reached. delete[] destructs all of the objects in the array, and deallocates the entire array. So, it is the responsibility of T's constructor and destructor to initialize and finalize T's data members properly.
The loop merely updates the content of the data members of the objects in the array. It is the responsibility of T::operator= to copy and free T's data members properly as needed.
There is no leak in this Set::operator= code. However there is a minor mistake - the loop needs to use set.size instead of size.
for (int i = 0; i < set.size; ++i)
The new array is allocated to set.size number of elements, so that is how many elements the loop needs to copy.
Using size for the loop, if the Set being assigned to is smaller than the Set being copied, the new array won't copy all of the elements. And if assigning to a Set that is larger, the loop will go out of bounds of both arrays.
If you are experiencing a leak, it would have to be in either T::operator= or in T::~T(), neither of which you have shown. Assuming Set::Set() and Set::~Set() are initializing and freeing data properly, this is.
Lets remove some complications from this code. Lets assume T == int and instead of storing many ints we only store one:
int_store& int_store::operator=(const int_store& set)
{
int* temp_data = new int; (1) allocate
try
{
*temp_data = *set.data; (2) assign
}
catch (...)
{
delete temp_data; (3) free temp
throw;
}
delete data; (4) free old
data = temp_data;
}
The method has one allocation int* temp_data = new int (1). It the tries to assign the other sets data to that temp value (2). When this fails the temp has to be deleted (3) otherwise we can replace the old data with the new data stored in temp_data and before doing that we have to delete the old data (4).
There is no allocation in the try block. All memory allocated in the function is either deleted (when assignment fails) or it is used to replace the old data, in which case the old data is deleted before.
If data is an array instead of a single int (almost) nothing changes and there is no leak. The elements you worry about are already allocated in the line T* data_temp = new T[set.size]; and then delete[] temp_data; will delete all of them.

Dynamic array crashing at constructor

I'm trying to implement a dynamic array of strings for educational purpose. The problem that I ran into is the program crashing whenever I try to add strings to the empty array in my constructor.
Array::Array(string dir, string dim)
{
size = 0;
ptr = new string[size + 1];
ptr[size] = dir;
size++;
ptr[size] = dim;
size++;
}
I have int size and string *ptr declared in my header file. I originally thought this to be a out-of-bounds problem, but after looking at this post, I fixed the initial allocation to size + 1, but the persisting problem seems to prove otherwise.
Changing the value of size does not change the size of the array.
You allocate an array of size 1.
Then you assign something to the first (only) element of that array.
Then you assign something to the second element of that array - but the array only has one element.
Also note that using new does not allocate a dynamic array. Once allocated, the size can't change.
As mentioned by Sid S, "changing the value of size does not change the size of the array."
And for your "inefficient" concern, a common trick that reflect to PaulMcKenzie and Daniel H's idea, is to use the doubling strategy. See the following C code for an simple idea:
#include <stdlib.h>
struct MyArray {
int capacity;
int size;
int *data;
}
/* any other functions you would use, eg: create, destroy of MyArray */
void push(struct MyArray myarray, int n) {
if (size == capacity) {
capacity *= 2;
data = realloc(data, capacity*sizeof(int));
}
/* add the element to the end of data and increase size */
}
In this way, instead of doing realloc every time there is an element added, you would have a lower runtime in average.
A detailed amortized analysis about doubling strategy can be found here.
Instead of string use pointer and allocate memory dynamically every time how much they need not 0 and then ++.
Array :: Array(char *dir,char *dim)
{
int l1,l2;
l1=strlen(dir);
l2=strlen(dim);
/**assume n1 & n2 are data member of "Array" class.**/
n1=new char[l1+1];// allocating memory dynamically
n2=new char[l2+1];
}
I hope it helps.

How do I manage to create a vector class in c++?

I was introduced to pointers, I quite get it. but I don't know how to store variables in the vector class using pointers.
Here is what I got from my understanding but how should I complete it?
class Vector{
int size;
int* element;
public:
vector(int x);
int size() const { return size }
};
first, you need to define a value that stores the current size - (number of elements inside the vector) - to be able to add values at the end of the vector.
int curr_vec_size;
also, the actual size of the vector should be saved in a variable to check every time you add a value that allocated memory is not full
int memory_size;
second, you need to allocate memory dynamically by using "new" in the constructor
vector(int size)
{
element = new int[size]; //allocating memory (array of integers)
memory_size= size; //size of allocated memory
curr_vec_size= 0; //no values in the vector
}
then you can make a method that takes an int value and adds it to the dynamic array.
void add_value(int passed_val)
{
if(curr_vec_size < memory_size)
{
element[curr_vec_size]=passed_val; //adding the value in the vector
curr_vec_size ++; //because you have added a new value
}
else
cout<<"vector is full \n";
}
Finally, don't forget to delete the memory you've allocated by using destructors that deletes the pointer to this allocated memory.
vector()
{
delete[] element;
}
To complete what you started, you simply need to use new[] operator to allocate memory to store your int values:
vector(int x)
{
size = x;
element = new int[size]; // this allocates an array of int with a size of "size"
}
Then, you can use element[i] to access i's element of your array.
You'll later need (it's a must) to release allocatd memory to prevent memory leak by implementing a destructor:
~vector()
{
delete [] element;
}
Note that you should (must) also also follow at least the rule of 3 to have you vector be copiable.

Unreadable memory when I create pointer array in c++

I'm new to pointers and dynamic memory. The program is supposed to be an array-based stack.
I need help with an error that occurs when I try to allocate memory to the pointer variable _pArr in the .h-file. When I try to create a new array with new double[] I get an 'unreadable memory' error message when I debug the code.
In the code down below I have created a new array in the .cpp-file and then copy it to the _pArr pointer, but I still get the same error.
class CStack{
public:
Stack(void);
~Stack(void);
.
.
.
private:
int _capacity=NULL;
int _size=0;
double* _pArr;
}
CStack::CStack(void)
{
if (_capacity == 0){
_capacity = 10;
}
else{
_capacity = _capacity * 2;
}
double* arr;
arr = new double [_capacity]
_pArr=arr;
delete[] arr;
}
Why does this error occur and how can I fix it?
Here's what your code is doing in the constructor:
// Allocate memory for an array and set arr to point to that array.
double* arr;
arr = new double [_capacity]
// Set _pArr to point to the same array.
_pArr=arr;
// Delete the array that arr and _pArr point to. If you
// deference _pArr after this, bad things will happen.
delete[] arr;
So you shouldn't be deleting the array immediately after you allocated it. (There's also a missing semicolon there.) Getting rid of that will likely fix your problem, but then you then need to delete the array in the class's destructor. And if you must use new[]/delete[], it would be far easier to do it in a single step, rather than creating a useless temporary variable:
CStack::CStack(void)
{
if (_capacity == 0){
_capacity = 10;
}
else{
_capacity = _capacity * 2;
}
_pArr = new double [_capacity];
}
CStack::~CStack(void)
{
delete[] _pArr;
}
Unrelated to your question, but that code that doubles _capacity doesn't do anything useful, as capacity will always be zero when the constructor is called. If you intended for this to expand an existing CStack, then you will need to put it in a method, and will need to worry about creating a new array for the stack, copying the contents of the old one into the new, and then deleting the old one.

possible problems after resizing dynamic array

Got little problem here.
I created dynamic array:
m_elements = new struct element*[m_number_of_elements];
for(int i = 0; i < m_number_of_elements; i++)
{
m_elements[i] = new struct element[m_element_size];
}
then I tried to resize existing array:
m_elements[m_number_of_elements] = create_more_elements();
m_number_of_elements++;
create_more_elements() is a function:
struct index* create_more_elements()
{
struct element* tmp = new struct element[m_number_of_elements]
return tmp;
}
In general, this piece of code works, but sometimes I get segfaults in different places.
Are segfaults connected with resizing?
Any thoughts?
You should use std::vector for it, then you can with new allocate memory for new struct and push her pointer to vector, if you deleting you should delete on pointer.
Try this:
std::vector<element> m_elements;
m_elements.resize(m_number_of_elements);
Don't go the route of manually managing an array unless absolutely necessary - std::vector will do a far better job, is better tested, proven, standardized and understood by legions of C++ programmers. See my code example - not even a single new or delete statement, yet this code also contains all required memory management.
P.S.: Since this question is tagged as C++, you don't have to write struct element whereever you use it as a type, just element will suffice. This suggests you are coming from C, so my advice: learn about the STL before you continue what you're doing, a single hour spent learning how to use the standard container classes can save you many days of manual tweaking, debugging and bug-fixing. Especially since once you've learnt one, you already know like 80% about all the others. :)
m_elements[i] = new struct element[m_element_size];
This creates an array of element of size m_element_size
To dynamically create a struct, just use new struct element or new element.
If don't have to initialize values in your array, you may even be better not storing pointers but actual objects in your array:
m_elements = new element[m_number_of_elements];
To "resize" an array, you actually have to allocate a new bigger array, copy the content of current array in the new one, and delete the old array.
// Allocate new array
element* newArray = new element[m_number_of_elements + 1];
// Copy old array content into new one
memcpy(newArray, m_elements, m_number_of_elements * sizeof(element)];
// Delete old array
delete[] m_elements;
// Assign new array
m_elements = newArray;
// Keep new size
m_number_of_elements += 1;
But you should definitely use std::vector which is simpler and smarter than that:
std::vector<element> elements;
// Add an element
Element element;
...
elements.push_back(element);
It is a wonder that you code even works. Basically what you are doing is overwriting memory after your initially allocated array. In C++ you can't resize the array, you can only delete it and new up a new one.
element** tmp = new element*[m_number_of_elements];
for(int i = 0; i < m_number_of_elements; i++)
{
tmp[i] = m_elements[i]
}
delete m_elements;
m_elements = tmp;
m_elements[m_number_of_elements] = create_more_elements();
m_number_of_elements++;
But, that is really crufty. As Svisstack points out, you should use std::vector or any other suitable standard container.
std::vector<element*> m_elements;
// ...
m_elements.push_back(create_more_elements());