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.
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.)
template<typename T> char* allocateSomething()
{
return reinterpret_cast<char*>(std::allocator<T>{}.allocate(1));
}
void deallocateSomething(char* mPtr) { delete mPtr; }
struct TestType { ... };
int main()
{
char* ptr{allocateSomething<TestType>()};
deallocateSomething(ptr);
return 0;
}
Is it guaranteed that deallocateSomething(ptr) will free all allocated bytes even if it has no knowledge of the typename T used when calling allocateSomething<T>()?
Or is it necessary to templatize deallocateSomething as well?
EDIT: I manually deal with the construction/destruction of the object.
EDIT 2: Will this work correctly?
template<typename T> char* allocateSomething()
{
return reinterpret_cast<char*>(std::malloc(sizeof(T)));
}
void deallocateSomething(char* mPtr) { std::free(mPtr); }
// ...
Is it guaranteed that deallocateSomething(ptr) will free all allocated bytes even if it has no knowledge of the typename T used when calling allocateSomething()?
No, nothing is guaranteed, it is undefined behaviour.
Or is it necessary to templatize deallocateSomething as well?
Yes, in general.
You must use matching allocation / deallocation methods. If you want to deallocate with delete then you must allocate with new. If you change the allocation so it doesn't use std::allocator (which you have no control over and no idea how it allocates) and instead allocates with new char[sizeof(T)] then you can safely deallocate with delete[] (but not delete! You must use the [])
You have to use matched allocation and free functions. I.E.
malloc with free
new with delete
new[] with delete[]
std::allocator<>::allocate with std::allocator<>::deallocate. Here the default version is specified to use new/delete, but it is not specified if it does some grouping of allocations or not (it could below some size and not above for instance), so what you do could work in an implementation, and not in another.
I would like to define a class with a vector data member. The class looks as follows
class A{
...
private:
std::vector<int> v1;
...
};
If I use operator new to allocate memory for class A, the program is OK. However, if I get the memory from the pre-allocated memory and cast the pointer to type A*, the program will crash.
A* a = new A;
A* b = (A*)pre_allocated_memory_pointer.
I need one vector with variable size and hope to get the memory for A from one pre-allocated memory. Do you have any idea about the problem?
An std::vector is an object that requires initialization, you cannot just allocate memory and pretend you've got a vector.
If you need to control where to get the memory from the solution is defining operator::new for your class.
struct MyClass {
std::vector<int> x;
... other stuff ...
void *operator new(size_t sz) {
... get somewhere sz bytes and return a pointer to them ...
}
void operator delete(void *p) {
... the memory is now free ...
}
};
Another option is instead to specify where to allocate the object using placement new:
struct MyClass {
std::vector<int> x;
... other stuff ...
};
void foo() {
void * p = ... get enough memory for sizeof(MyClass) ...
MyClass *mcp = new (p) MyClass();
... later ...
mcp->~MyClass(); // Call destructor
... the memory now can be reused ...
}
Note however that std::vector manages itself the memory for the contained elements and therefore you'll need to use stl "allocators" if you want to control where the memory it needs is coming from.
It is not enough to cast the pre-allocated memory poiner to your user-defined type unless this UDT is "trivial".
Instead, you may want to use the placement new expression to actually call the constructor of your type at the provided region of memory:
A* b = new(pre_allocated_memory_pointer) A();
Of course, you need to ensure that your memory is properly aligned and can fit the whole object (i.e. its size is >= sizeof(A) ) beforehand.
Don't also forget to explicitly call the destructor for this object before de-allocating the underlying memory.
b.~A();
deallocate(pre_allocated_memory_pointer);
As I understand your question you are confusing the data memory of the std::vector with the memory it takes up as a member.
If you convert pre_allocated_memory_pointer to A*, then no constructor got called and you have an invalid object there. This means that the v1 member will not have been constructed and hence no memory has been allocated for the vector.
You could use placement new to construct the A instance at the pre_allocated_memory_pointer position but I doubt that is what you want.
In my opinion you want a custom allocator for the vector that gets the memory for the vector's data from the preallocated memory pool.
Hello So I'm experimenting with creating objects and arrays with preallocated memory. For instance I have this following code:
int * prealloc = (int*)malloc(sizeof(Test));
Test *arr = new(prealloc) Test();
Where test is defined as follows:
class Test {
public:
Test() {
printf("In Constructor\n");
}
~Test() {
printf("In Destructor\n");
}
int val;
};
In this scenario if I call delete it will actually release the memory which is bad, b/c maybe I'm using some type of memory manager so this will sure cause some problems. I searched in the internet and the only solution that I found was to call the destructor explicitly and then call free:
arr->~Test();
free(arr);
Is there another way to do this? is there perhaps a way to call delete and tell it to just call the destructor and not to release the memory?
My second problem was when working with arrays, like the previous example you can pass to new the pre-allocated memory:
int * prealloc2 = (int*)malloc(sizeof(Test) * 10);
Test *arr2 = new(prealloc2) Test[10];
If I call delete[] it will not only call the destructor for each element in the array but it will also release the memory which is something I don't want. The only way I have found that it should be done is to go through the array and call the destructor explicitly, and then call free. Like with the regular none array operators is there a way to tell the operator to just call the destructors without releasing the memory?
One thing I did notice was that the new operator for an array will actually use the first 4 bytes to store the size of the array (I only tested this in visual studio with a 32 bit build) That would help me know how many elements the array has but there is still one problem. What if the array is a pointer array? for example:
Test **arr2 = new Test*[10];
Could someone help me out with these questions please.
It's normal and expected to directly invoke the destructor to destroy objects you've created with placement new. As far as any other way to do things, about the only obvious alternative is to use an Allocator object (which, at least 99% of the time, will just be a wrapper around placement new and directly invoking the destructor).
Generally speaking, you do not want to use new[] at all. You typically want to allocate your raw memory with operator new (or possibly ::operator new) and release it with the matching operator delete or ::operator delete.
You create objects in that memory with placement new and destroy them by directly invoking the destructor.
There is no other way to do it but to explicitly call the destructor as delete will also attempt to free the memory.
Using preallocated memory with placement new should be fairly rare in your code - a typical use case is when you're dealing with direct memory mapped hardware interfaces when you want/need to map an object on top of a fixed memory address - and is something I'd normally consider a code smell.
If you want to tweak the memory management for a specific class, you're much better off either using an STL container with a custom allocator or overload operators new and delete for that specific class.
Yes, this is the only way to do it. There is an asymmetry in being allowed to define new but not delete. [ Well, you can do the latter but it can only get called when new throws an exception (not handled properly below!)
You can use a templated destroy to achieve the same result:
class Test
{
public:
Test() {
printf("In Constructor\n");
}
~Test() {
printf("In Destructor\n");
}
int val;
};
class Allocator
{
public:
static void* allocate(size_t amount) { return std::malloc(amount);}
static void unallocate(void* mem) { std::free(mem);}
static Allocator allocator;
};
Allocator Allocator::allocator;
inline void* operator new(size_t size, const Allocator& allocator)
{
return allocator.allocate(size);
}
template<class T>
void destroy(const Allocator& allocator, T* object)
{
object->~T();
allocator.unallocate(object);
}
int main()
{
Test* t = new (Allocator::allocator) Test();
destroy(Allocator::allocator, t);
return 0;
}
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];