I am making a templated class holding a variable named array, and the user is allowed to change the size of this array. I am wondering how to deal with memory leaks?
templated <class T>
class MyClass{
long *array;
MyClass(long min, long max);
void Add(long n);
}
MyClass<T>::MyClass(long min, long max){
array= new long[min];
}
void MyClass<T>::Add(long n){
delete [] array;
array = new long[n];
}
(Yes, I know a vector would be better, but I must use arrays.)
Does this effectively change the size of the array to n and account for memory leaks?
When you have a class that dynamically allocates memory, you need a destructor to deallocate it. In your case, your MyClass destructor needs to also delete[] array;
Related
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 years ago.
Improve this question
I am learning the dynamic memory allocation process in c++.
1.How to declare dynamic memory allocation of an array without prior knowledge of the size?
2.Suppose I use a variable to dynamically allocate memory to the array but later on in the program the size of array is reduced.Will there be automatic de-allocation of memory?
If not then how to update it?
Please spare me if these are silly questions.If you answer please include an example program.
As Cheers and hth said in the comments, the best way is to use std::vector, it takes care of the memory management itself.
How to declare dynamic memory allocation of an array without prior knowledge of the size?
The idea is, you do not allocate any memory if you do not know the size, and when adding elements to it, you can increase the memory, see example below.
Implementing a class that works like std::vector:
vector uses two sizes, one is the size which is the number of elements your vector is currently holding, capacity is the number of elements which your vector can hold (memory is allocated for capacity)
Pre-requisites: I'm assuming that you know the basic memory allocation and de-allocation, that you can allocate memory using new operator, and de-allocate using delete operator.
Note: I'm implementing some methods for MyIntVector that uses only int array for simplicity, but you can always implement a templated class.
Implementation:
You can provide a default constructor for your class, which doesn't allocate any memory, sets the size and capacity to 0 (client programs can use this constructor, if the size is not known).
You can also provide a constructor which takes a size that will be used as the starting size, when creating the MyIntVector
Also provide a destructor to completely de-allocate the allocated memory.
class MyIntVector{
size_t _size;
size_t _capacity;
int* _data;
public:
MyIntVector(){
_size = 0;
_capacity = 0;
_data = nullptr;
}
MyIntVector(size_t size){
_size = size;
_capacity = size;
_data = new int[_capacity];
}
~MyIntVector(){
if (_data){
delete[] _data;
}
_data = nullptr;
_size = _capacity = 0;
}
};
Now, if you want to add some element to your MyIntVector, you can implement a push_back function, like std::vector does.
class MyIntVector{
//...
void push_back(int elem){
if (_size >= _capacity){
// Oops, we're out of memory, let's make room for this elem first
resize(_capacity > 0? _capcity * 2 : 10);
}
// Now, there's enough memory to hold this elem
_size++;
_data[_size - 1] = elem;
}
void resize(size_t newCapacity){
if (newCapacity != _capacity){
if (_size == 0){
_capacity = newCapacity;
_data = new int[_capacity];
}
else {
// create a temporary array to hold the elements of _data
int* temp = new int[_size];
for (size_t i = 0; i < _size; i++)
temp[i] = _data[i];
// de-allocate the memory of _data
delete[] _data;
// allocate memory in _data with newCapacity
_capacity = newCapacity;
_data = new int[_capacity];
// copy the elements of temporary array back in _data
for (size_t i = 0; i < _size; i++){
_data[i] = temp[i];
}
// done with the temporary array, de-allocate it.
delete[] temp;
temp = nullptr;
}
}
}
//...
};
push_back function:
The push_back function, that I've implemented in above example, it sees whether the new element can be added to the MyIntVector without any need to allocate any new memory or not, if not, it just increases the _size by 1 and copies the element. If there's a need for new memory, it calls the resize method with doubled capacity (in case, there's already some allocated memory there.) or some hard-coded value, say 10 (in case, where there's not any previously allocated memory)
resize function:
The resize function takes a newCapacity as argument, and allocates the required memory in _data, mainly it creates a temporary array to hold the elements of _data and then de-allocates the memory in _data, allocates a larger memory in _data, copies back the from temp to _data and de-allocates the temp array.
Note: This implementation of resize should only be used when increasing the _capacity, it should not be called for decreasing the _capacity
So, the idea is, that you can increase the size of the array, by using a temporary array for holding the elements, de-allocating the previous array, allocating a new array, and copying back the elements from temporary array. You can implement different methods that std::vector provides for your exercise.
If you don't want to de-allocate and re-allocate the memory every time you increase the capacity, you can always implement a Linked List, here's a tutorial for implementing a Linked List, but there's a drawback of Linked List, that you can't randomly access elements from the Linked List like you can from an array.
Suppose I use a variable to dynamically allocate memory to the array but later on in the program the size of array is reduced.Will there be automatic de-allocation of memory?
There's no automatic de-allocation of memory, you'll have to manually decrease the _capacity if you want.
You can add a pop_back method in your MyIntVector class to remove an element.
class MyIntVector{
// ...
int pop_back(){
if (_size == 0){
// You can throw some exception here if you want.
throw exception("No elements found");
}
_size--;
return _data[_size];
}
// ...
};
You can manually decrease the _capacity before returning the element.
You can also provide an implementation of subscript operator [] for your MyIntVector class to provide random access in the array.
If you do choose to dynamically allocate memory, you will need to use the operator new[] for an array, which is considered a different operator in c++ than new for non-array types.
1) Here is an example of how to dynamically allocate an array:
int length = 10;
int *myArray = new int[length];
// do something
myArray[0] = 42;
When you are done, you will need to release the memory with delete[]:
delete[] myArray;
2) No, there is no automatic de-allocation of dynamically allocated memory in C++ unless you are using smart pointers (What is a smart pointer and when should I use one?).
If you want to resize your array, you will have to do it manually:
delete[] myArray;
int newLength = 5;
myArray = new int[newLength];
As #frameworks pointed out, you will need the operator new[] to allocate memory for an array and you need to call delete[] to free that memory. If there is no delete[] for a new[], your program will leak memory. (If there is more than one delete[] called for a single new[], your program will segfault / crash.)
However, it is not always trivial to ensure that memory allocated with new[] (or new) gets always cleaned up by a corresponding delete[] (or delete), and only gets cleaned up once. That is especially true for code with complex control flow, code where pointers get passed around or situations where exceptions can be thrown between new[] and delete[].
In other words: Wherever there is a new, there probably is a leak.
So to save you all that trouble of manual memory management, use std::vector instead. This container takes care of the memory management for you.
Whenever you are tempted to write something like
int *myArray = new int[3];
//do something with myArray
delete[] myArray;
you should write
std::vector<int> myArray;
//do something with myArray
instead. You do not need a delete here and methods like std::vector::push_back() take care of the required size adjustments for the underlying structure, should it not provide enough space to accomodate all pushed values. You can also use std::vector::shrink_to_fit() to remove unused elements. (However, shrink_to_fit is a non-binding request to reduce the internal size. That is, results may vary between compilers.)
I'm trying to implement an array class which can be dynamically sized and it will allocate memory without initialising values. My question is relating to how I deallocate the memory later:
Can I implement my code in this way?:
template<typename Type>
class Array {
Type *values;
Array(int size) : values(new (size * sizeof(Type)) char()) {}
~Array() {
delete[] values;
}
}
Or will I need something like this?:
template<typename Type>
class Array {
Type *values;
void *memory;
Array(int size) : memory(new (size * sizeof(Type)) char()), values(memory), size(size) {}
~Array() {
delete[] memory;
}
}
NOTE!!!! I am aware that this will allocate memory without initialising any Type objects. This is the intended behavior of my code. Also the code above is not my complete implementation, it's only the code that will allocate and deallocate the memory because that is what I'm interested in with this question.
Upon further research I found that I should use malloc() and free() to do what I'm trying to do. Thank you to all answers and commenters.
You can use a Type* to make life easier, and use operator new to get memory without constructing objects.
Type* values; //No aliasing/casting issues
Array(int size) : values((Type*)::operator new(sizeof(Type) * size)) //...
~Array() { /*clean up extant objects*/ ::operator delete(values); }
Then you can use placement new and explicit destructor calls to manage the elements. Also, placement new doesn't allocate memory. It constructs an object at the specified address.
Placement new does not actually allocate any memory. It is merely syntax for calling a constructor with already-allocated memory.
In your destructor, you must manually call the destructors for each present element, then deallocate the memory in the same way it was allocated. Be very careful about exceptions.
You should probably use std::allocator<T>::allocate and std::allocator<T>::deallocate, but you could use malloc/free, new char[]/delete[] (char*)arr, or ::operator new/::operator delete.
I am receiving "Segmentation fault" when I try to populate my array. So I was think of declaring the arrays size in the class to declare the array's size so it can allocate the space, but I receive.
error: invalid use of non-static data member ‘Array::Size’ error: from
this location
//Current
class Array{
public:
int Size;
int Range;
int Array[];
};
//Problematic
class Array{
public:
int Size;
int Range;
int Array[Size];
};
or there any other method of preventing an Segmentation fault?
You're attempting to use a C idiom, where the last member of a structure is a very short array (one or, if permitted by the compiler as a nonstandard extension, zero elements) and extra memory is allocated for the structure so elements beyond the end of the declared array can be accessed. This was originally a nonstandard trick, known as the struct hack, or in some compilers as zero-length arrays. It was standardized in C99 as flexible arrays.
When you used this idiom, you needed to allocate additional memory for the array, e.g. sizeof (struct Array) + sizeof (int) * number_of_elements. Otherwise you wouldn't have memory allocated for more elements than you actually declared (usually one or zero), and you'd get undefined behaviour when you tried to access elements beyond that.
However, you're writing C++, not C99. Even if your compiler allows you to do this, relying on it would be very bad practice, and a lot more awkward C++ than in C.
Either store a pointer in the class and allocate the appropriate amount of memory in the constructor, and deallocate it in the destructor, or use a C++ class that will manage the memory for you, e.g. std::vector.
Here's an example of using std::vector:
#include <vector>
class Array{
public:
int size;
int range;
std::vector<int> array;
Array(int size, int range) :
size( size ),
range( range ),
array( size, 0 )
{
}
};
Both these definitions
//Current
class Array{
public:
int Size;
int Range;
int Array[];
};
//Problematic
class Array{
public:
int Size;
int Range;
int Array[Size];
};
are invalid. The first one is invalid because class definition may not containg incomplete non-static data members. You may not write int Array[];
The second one is invalid because 1) data member Size has undefined value and 2) the size of an array shall be a constant expression.
Instead of declaring array you could use a pointer and dynamically allocate an array of the required size.
You can use pointer.
class Array{
public:
int Size;
int Range;
int* Array;
};
In the constructor, you can allocate memory for it. Or maybe you can do it in a member function.
Array::Array(/*parameters*/){
/* code */
Array = new int [Size] //After Size is initialized or assigned.
}
In the destructor, you should use delete[] Array to deallocate the memory.
I want to use malloc as it's memory assignment happens in O(1) rather than O(n) with the following code:
MyQuickInitArray(int size)
{
A = new T[size];
}
When A is of type:
T* A
I thought initializing an array of pointers will take O(1) because pointers are primitives but i doubled checked and the code above actually goes size times to the constructor of T. If this problem can be avoided by either malloc or something that i'm missing than it would be great.
As you correctly point out, new T[n] calls the constructors, whereas malloc() doesn't.
If you don't want to call the constructors (why?), then clearly new[] isn't right for you.
If, however, you do want to have the constructors called, then there is no way to get around the o(n) complexity.
If what you're looking for is an array of pointers to T, the correct syntax is as follows:
T** A = new T*[size];
This will not call T's constructor.
I thought initializing an array of pointers will take O(1)
You are not allocating an array of pointers, you are allocating an array of objects of type T with your code:
T *A = new T[size];
to allocate an array of pointers you need
typedef T *T_Ptr;
T_Ptr *A = new T_Ptr[size];
I'm using a typedef so that the syntax is cleaner.
If you have allocated space for your objects you can properly initialize them using C++'s "placement new" operator; in this case new is not allocating memory however it will call the constructor. http://www.cplusplus.com/forum/general/55150/ has an example, but generally you do not want to do this, you want to allocate your objects in a non-time-critical portion of your program instead.
What about overloading the new
void* class_name::operator new(size_t size)
{
cout<<"Allocating memory for object \n";
void *p;
p=malloc(size);
if(p==NULL)
cout<<"Memory allocation error\n\a";
else
return p;
}
For array
void* class_name::operator new[](size_t size)
{
cout<<"Allocating array of size "<<size<<endl;
void *p;
p=malloc (size);
if(p==NULL)
{
cout<<"Memory allocation error \n";
}
return p;
}
How it is used :
Class_name *objptr1,*objptr2;
objptr1=new class_name(10);
objptr2=new class_name[10];
I have to write a stack class template using arrays in C++ for my assignment.
Here is my code:
#include <iostream>
template <typename Type>
class Stack {
private:
int top;
Type items[];
public:
Stack() { top = -1; };
~Stack() { delete[] items; };
void push(Type);
bool isEmpty() const;
Type pop();
Type peek() const;
};
int main (int argc, char *argv[]) {
Stack<double> st;
return 0;
}
template<typename Type>
void Stack<Type>::push(Type item) {
top++;
if(top == sizeof(items) / sizeof(Type)) {
Type buff[] = new Type[top];
std::copy(items,items+top,buff);
delete[] items;
items = new Type[2*top];
std::copy(buff,buff+top,items);
delete[] buff;
}
items[top] = item;
}
template<typename Type>
bool Stack<Type>::isEmpty() const {
return top == -1 ? true : false;
}
template<typename Type>
Type Stack<Type>::pop() {
//TODO
return items[top--];
}
template<typename Type>
Type Stack<Type>::peek() const{
//TODO
return items[top-1];
}
It compiled fine using "g++ -Wall", however when I run the program, I got this error:
* glibc detected * ./lab3: munmap_chunk(): invalid pointer: 0x00007fff41a3cdf8
After trying out a bit with GDB, I found out the error arose from the line:
'free[] items' in the destructor.
I don't understand why freeing an array results in a memory leak? Any clues?
You should only delete[] what you have explicitly allocated with new[]. Your items member is not a dynamically allocated array, so you must not free it as if it were.
On the other hand, you have
Type items[];
which doesn't actually allocate any space in your object instances for the stack.
I don't think that's a memory leak! It's a crash occurring because you deleted a non-heap object.
You haven't new'd items, so you can't delete it in the destructor. Assuming you require 'Stack' to be a dynamically sized class then the items array must be dynamically allocated in which case the declaration for items should be
Type *items; (as hacker mentions above)
and the constructor should call 'items = new Type[ARRAY_SIZE]' to allocate the memory, or at the very least initially assign the 'items' pointer to NULL.
Edit based on comments -
To complete and secure the memory allocation responsibilities for this class you should also include a copy constructor and an assignment operator which allocates new memory and copies the values stored in items to the new object. This avoids the pointer simply being copied by the compiler generated copy constructor or assignment operator which would lead to multiple objects pointing to the same dynamically allocated memory. Upon destruction of the first of these objects this memory will be deleted. Further use of the now deleted memory by the other objects which share the pointer would be likely to result in a crash.
Rather than adding code here I refer you to the code in Martin's answer for this question.
items is a pointer to Type. This pointer must be initialised
before it is used (it is a wild pointer as it is). That
is why your program crashes. That it happens in the
destructor is a coincidence. It could just as well have
happened earlier. E.g. in push() memory is overwritten in the
location that items happens to point to.
You need to allocate memory for a number of elements. See below.
You already have the logic for the dynamic growth of the
array in place. But an array in C++ does not know its size
(it is just a pointer of some type). You need to keep track
of the current capacity/size. Thus instead of
sizeof(items)
define a new member to hold the current capacity.
E.g.:
int capacity;
and in the constructor:
capacity = 100;
items = new Type[capacity];
The last line allocates a number of elements and is the main solution to your problem.
And in push():
if (top == capacity / sizeof(Type)) {
The logic for reallocation will need to change. E.g.:
capacity *= 2;
items = new Type[capacity];
instead of
items = new Type[2*top];
I have just sketched out a solution here. Yon can fill in the details.
Note: you have only one instance of Stack in your program; body of main() is:
Stack<double> st;
If you instead were to assign one instance of Stack to another instance, e.g.
{
Stack<double> st;
Stack<double> st2 = st; //Copy constructor will be invoked for st2. If not defined the compiler generated copy constructor will do a copy of the pointer value.
Stack<double> st3;
st3 = st; //Assignment operator will be invoked for st3. If not defined the compiler generated assignment operator will do a copy of the pointer value.
}
then the copy constructor (to work for st2) and the
assignment operator (to work for st3) for Stack must be
defined properly (make a copy of the referenced member
items). Otherwise a double or triple delete in the destructor and
undefined behavior (e.g. a crash) will be the result when
st, st2 and st3 goes out of scope at the close brace.
The first thing I've seen is that you're likely to mean Type *items, not Type items[].
And then you don't want to (can't) use sizeof on the dynamically allocated data.