Memory leak prevention C++ (Am I right or wrong?) - c++

I have searched the internet for hours and hours and came up with these steps to prevent memory leaks (w/o use of smart pointers and advance tools as such). Please let me know if there are any issues with any of the below findings... Thanks in advance.
for each new there should be a delete.
if the new is inside a class putting the delete in the destructor would take care of the dynamically allocated memory.
if you do a new object obj from class foo in main then in you would have to explicitly delete obj.
if you have multi-dimentional dynamic arrays you have to delete both levels. (I am a bit unsure of this being safe... I thought just one delete for the ptr might do it but after thinking harder I figured it is a dynamic array of dynamic arrays and should treat each as a dynamic array separately).
ex 1.
char *ptr;
ptr = new char[size];
// Some Code here
delete ptr;
ex 2.
//foo.h
class foo {
private:
char * ptr;
public
foo();
~foo();
};
// foo.cpp
foo::foo()
{
ptr = new char[100];
for (int i = 0; i < 100; i++)
ptr[i] = i;
}
foo::~foo()
{
delete ptr;
}
ex 3.
//main.cpp foo class stays the same as ex 2.
int main()
{
foo obj = new foo();
// Some code here
delete obj;
return 0;
}
ex 4.
//foo.h
class foo {
private:
char ** ptr;
public
foo();
~foo();
};
//foo.cpp
foo::foo()
{
ptr = new char[100];
for (int i = 0; i < 100; i++)
ptr[i] = new char[100];
for (int i = 0; i < 100; i++)
for (int j = 0; j < 100; j++)
ptr[i][j] = i;
}
foo::~foo()
{
for (int i = 0; i < 100; i++)
delete ptr[i];
delete ptr;
}

Below are some tips you need to take care while managing memory on your own:-
First and most important of all is:-
1) Always use delete with new and delete[] with new[].Mixing these would result in undefined behavior.
2) Never combine malloc/delete OR new/free.
For your question "if you have multi-dimentional dynamic arrays you have to delete both levels."
Yes you have to take care of them.
Things you mentioned are correct i.e you have to explicitly delete the memory whenever you are allocating memory in heap. Though you missed one important aspect which revolves around RAII. Let's say there's a function in which you are taking care of allocation/deallocation.
void fun ()
{
1) int* ptr;
2) ptr = new int; //allocated memory.
3) ... //do something with ptr.
4) delete ptr; //done with ptr now delete it.
}
Now you are doing good by deleting memory which you allocated using new. However, there could be a chance that program execution won't even reach to your delete. ( in case exception is thrown in STEP#3). That case would constitute a memory leak.
To care of these issues we have smart pointers based on the concept of RAII. They will automatically delete the memory they are referring to whenever go out of scope that means you don't need to take care of memory management.

for each new there should be a delete.
Yes. And to be more precise: Every new must have a matching delete, and every new[] must have a matching delete[]
if the new is inside a class putting the delete in the destructor would take care of the dynamically allocated memory.
No! You also should take care on what should happen for copies through copy constructor or assignment, unless you explicitly forbid these operations. See What is The Rule of Three? for more details.
if you do a new object obj from class foo in main then in you would have to explicitly delete obj.
?? See 1.?
if you have multi-dimentional dynamic arrays you have to delete both levels. (I am a bit unsure of this being safe... I thought just one delete for the ptr might do it but after thinking harder I figured it is a dynamic array of dynamic arrays and should treat each as a dynamic array separately).
Yes, you always have to take care about proper memory management, if you're using raw pointers, no matter how many levels of indirection.
My final advice is, leave your head clean about bothering with such questions, but use the stuff provided with the <memory> smart pointers, and standard c++ containers.
Don't use new/delete directly, unless you're a 100% sure what you're doing, and you're a 200% sure you really need to do it.
As you're asking about actual issues:
(ex 2.) The delete doesn't match the new char[]
ptr = new char[100];
// ^^^^^
foo::~foo() {
delete [] ptr;
// ^^ put the square brackets there
}
same for (ex 4.)

Related

Do I need to call delete[] on a member array that is declared with a constant value in class decleration?

here is an example of a code hopefully will demonstrate my confusion
#define MAX_LENGTH 5
class Bar
{
private:
Foo *_myFooArray[MAX_LENGTH];
public:
Bar()
{
for (int i = 0; i < MAX_LENGTH; ++i)
{
_myFooArray[i] = new Foo(i);
}
}
};
Since I am not creating the array with new I don't think I can use delete[] but what if I want to delete the objects that are allocated dynamicly? do I iterate through the array and delete them one at a time? as such;
~Bar()
{
for (int i = 0; i < MAX_LENGTH; ++i)
{
delete _myFooArray[i];
}
}
I will probably hear some of you screaming at me Use Vectors!! I appreciate that. I just want to learn. Just for completeness, If for some reason I have to use the array as mentioned above, are there anything that I need to pay extra attention besides deleting the array correctly?
One of the rules in C++ when not using smart pointers is "every new needs a delete."
You must use the destructor from your question to avoid leaking memory. The array itself is not allocated using new so you do not need to delete it.
No, you do not delete [] the array. The array is part of your instance's data. The elements you allocate specifically are not, so you should delete them.

do I HAVE to use the new keyword?

This isn't the code I'm working on but it's the gist of what I want to do.
object *objects; int totalObjects;
void addObject(object o)
{
objects[totalObjects] = o;
totalObjects++;
}
It's giving me an access error when I try this:
Unhandled exception at 0x00e8a214 in crow.exe: 0xC0000005: Access violation writing location 0xcccccccc
Am I going to have to use 'new' and if so do I have to create a new array to copy to every time? Can I just add or take elements from the array I'm using?
Why don't you just use std::vector?
std::vector<object> objects;
void addObject(object o)
{
objects.push_back(o);
}
..or
void addObject(const object &o)
{
objects.push_back(o);
}
to remove additional copying.
When it comes to implementing your own dynamic array without std::vector, Yes. you need to allocate new memory, and copy your array to new memory block. Here's my example code with malloc and placement new.
#include <stdlib.h> // for malloc/free
#include <new> // for placement new, std::bad_alloc
object *objects = nullptr;
size_t totalObjects = 0;
void addObject(const object &o)
{
object *old_objects = objects;
size_t old_size = totalObjects;
size_t new_size = totalObjects + 1;
object *new_objects = (object *)malloc(sizeof(object) * new_size);
if (new_objects == nullptr)
throw std::bad_alloc();
size_t i;
try
{
for (i = 0; i < old_size; ++i)
{
new (&new_objects[i]) object(old_objects[i]); // placement new
}
}
catch (...)
{
// destroy new_objects if an exception occurs during creating new_objects
for (size_t j = 0; j < i; ++j)
{
new_objects[i].~object();
}
free(new_objects);
throw;
}
objects = new_objects;
free(old_objects);
}
(I haven't tested the code yet >o<)
Note that I used malloc and placement new, not new operator. It's impossible to call copy constructor of each element of the dynamic array with array-new.
However, if your object is TriviallyCopyable, you can use realloc. It can be more efficient, because realloc can just expand memory block, without copying - if the memory is enough.
..And you can select multiple lines and just press TAB in Visual Studio (..or many other editors).
You declared an object pointer, but not yet allocated the actual memory to store object objects. Your assignment statement merely tries to copy the input object o into an unallocated array member.
This is why you should use new before the assignment. The new operator asks the system to allocate some memory in the required size, then return the address of that memory and assign it to the pointer. Then, the pointer points to that newly allocated memory and the assignment (or copying) can be made.
When you finished using the array space, you should free the allocated memory using delete.
Okay, I'm going to add an answer to my own question. Let me know if this is bad etiquette. I just wanted to post some of my own code to duel with yours.
#include <vector>
std::vector<object> objects;
okay so I want to have two arrays (vectors) for the objects and double for distances so I may end up with
std::vector<double> distances;
void swap(unsigned int a, unsigned int b)
{
objects.swap_ranges(a,b);
distances.swap_ranges(a,b)
}
I'm going by the cplusplus.com reference for this function so let me know if I have it wrong. I'm going to go through it and completely redo my code.
Is there a type like the matrix that will let me hold data of different types so I don't have to invent a new object to handle each one individually?
If what you wrote is the most efficient and fast way to do this then I'll make a new class to hold both items.
thanks :)

Do i have to delete wchar_t that i allocated with new?

Lets say i have a class like this:
class LolClass {
LPWSTR* stuff;
LolClass::LolClass(LPWCHAR lols) {
stuff = new LPWSTR[100];
for (int i = 0; i < 100; i++) {
stuff[i] = new wchar_t[wcslen(lols)+1];
wcsncpy(stuffs[i], lols, wcslen(lols)+1);
}
}
LolClass::~LolClass() {
delete[] stuff;
}
}
so if i call
LolClass* veryfunny = new LolClass(L"lol!");
it would make me 100 lol's, problem is when i call
delete veryfunny;
it deletes the pointer array but not the individual wchar_t's, if i try and loop through the wchar_t's and delete them then i just crash and i dont know what to do since i know for fact that they are still there even after i delete veryfunny (i checked by passing one of the pointers outside the class)
Thanks!
If you call
LolClass* veryfunny = new LolClass(L"lol!");
then you will eventually need to call:
delete veryfunny;
But for arrays, you need delete[]: stuff[i] = new wchar_t[n]; delete [] stuff[i];
In your code, you need to first loop over stuff and delete[] the elements, and then delete[] the array stuff itself.
Or just use a std::vector<std::wstring>.
Update: From what I understand, you're not actually worried about deleting *veryfunny, but rather about how to write a correct destructor for LolClass. Note that the destructor also gets invoked for global and automatic objects, e.g. { LolClass x(L"hello"); } /* boom */, so it doesn't really matter how you instantiate an object for LolClass. You have to get the class right in any case.
David Schwartz wins after all, the wchar's didnt let themselves be deleted because they werent null terminated

stl + memory management question

For example, i have next code:
#include <set>
using namespace std;
struct SomeStruct
{
int a;
};
int main ()
{
set<SomeStruct *> *some_cont = new set<SomeStruct *>;
set<SomeStruct *>::iterator it;
SomeStruct *tmp;
for (int i = 0 ; i < 1000; i ++)
{
tmp = new SomeStruct;
tmp->a = i;
some_cont->insert(tmp);
}
for (it = some_cont->begin(); it != some_cont->end(); it ++)
{
delete (*it);
}
some_cont->clear(); // <<<<THIS LINE
delete some_cont;
return 0;
}
Does "THIS LINE" need to be called before deleting some_cont for avoiding memory leaks or destructor will be called automatically?
You don't need to call it, destructor will be called for sure.
No, there is no need to clear the set before destroying it.
Note that there is very rarely a need to allocate an std::set (or any standard container) manually. You'd be much better off just putting it in automatic storage and letting C++ handle the cleanup for you:
So instead of
set<SomeStruct *> *some_cont = new set<SomeStruct *>;
use
set<SomeStruct *> some_cont;
then change all some_cont-> to some_cont. and remove the delete some_cont (the container will be destroyed when main exits automatically.
The advantage to do things this way are:
You don't need to remember to delete the container, and
You don't need to do an expensive memory allocation up front.
It's also far more idomatic C++ to put things in automatic storage.
No, you don't need to explicitly clear a set before destroying the set.
OTOH, you do have a number of other problems ranging from lousy (Java-like) design, to incorrect syntax, to a missing operator to lots of potential memory leaks. While some of the design might make sense in Java or C#, it's a really poor idea in C++. Once we get rid of the most egregious problems, what we have left is something like this:
#include <set>
struct SomeStruct
{
int a;
SomeStruct(int i) : a(i) {}
bool operator<(SomeStruct const &other) const { return a < other.a; }
};
int main ()
{
std::set<SomeStruct> some_cont;
for (int i = 0 ; i < 1000; i ++)
{
SomeStruct tmp(i);
some_cont.insert(tmp);
}
return 0;
}
No it is not, this will be done automatically in the set's destructor.
The STL containers automatically free any memory they own. So in your case the place allocated to store your SomeStruct* will be freed by the destructor of set. Note that the destructor of set does not call any destructors of SomeStruct, so it's good you iterate over them to delete them yourself.

How to delete an array of pointers

I've been brushing up on my C++ as of late, and I have a quick question regarding the deletion of new'd memory. As you can see below i have a simple class that holds a list of FileData *. I created an array to hold the FileData objects to be pushed into the list. When ReportData is destructed I loop through the list and delete each element. My question is, how can i delete the array when I'm done using reportData, so that I do not have any memory leaks?
Report.h
class REPORTAPI ReportData {
public:
ReportData()
{
}
virtual ~ReportData()
{
printf("Starting ReportData Delete\n");
for (list<FileData*>::iterator i = ReportFileData.begin(), e = ReportFileData.end(); i != e; )
{
list<FileData*>::iterator tmp(i++);
delete *tmp;
ReportFileData.erase(tmp);
}
for (list<SupressionData*>::iterator i = ReportSupressionData.begin(), e = ReportSupressionData.end(); i != e; )
{
list<SupressionData*>::iterator tmp(i++);
delete *tmp;
ReportSupressionData.erase(tmp);
}
ReportFileData.clear();
ReportSupressionData.clear();
printf("Finished ReportData Delete\n");
}
list<FileData *> ReportFileData;
list<SupressionData *> ReportSupressionData;
}
extern "C" __declspec(dllexport) FileData* __stdcall createFileData(string fileName, long recordCount, long addPageCount)
{
return new FileData(fileName, recordCount, addPageCount);
}
Main.cpp
ReportData *reportData = createrd();
if (reportData != NULL)
{
CreateFileDataFunc createfd (reinterpret_cast<CreateFileDataFunc>(GetProcAddress (dll, "createFileData")));
const int num_files = 5;
FileData *fileData[num_files];
char buff[256] = {'\0'};
for (int i = 0; i < num_files; i++)
{
sprintf(buff, "test: %d", i);
fileData[i] = createfd(buff, 1, 1);
reportData->ReportFileData.push_back(fileData[i]);
}
delete reportData;
reportData = NULL;
delete [] fileData; // this is throwing an access violation error:
//EAccessViolation: 'Access violation at address 326025AF. Write of address 00000008'.
}
--- I removed the delete oprations from the ReportData dtor
and I'm now looping and deleting:
for(int i = 0; i < num_files; i++)
{
delete fileData[i];
}
This is easier to understand then having to rely on a separate object's dtor to clean up memory.
You don't. fileData is an automatic (stack) variable. You didn't allocate it with new, so you don't delete it.
[Edit: also I'm not sure, but I think you could face problems deleting those FileData objects from main.cpp, considering that they were allocated in some dll. Does the dll provide a deleter function?]
Your array is not dynamically allocated, so you don't need to delete it. Each element, however, is pointing to a dynamically allocated object (from your comment):
createfd is a function pointer that returns a new instance of FileData though
What you need to do is loop over the elements of the array, and free each of them.
for(int i = 0; i < num_files; i++)
{
delete fileData[i];
}
// allocate on the stack, no manual delete required
FileData *fileData[num_files];
// allocate on the heap, must delete after usage
FileData *fileData = new FileData[num_files];
// ..
delete [] fileData;
Have you thought about wrapping FileData* with a smart pointer?
The problem with your dtor is that an exception will cause a memory leak (with some other problems relating to exceptions leaking out of dtor's).
"My question is, how can i delete the array when I'm done using reportData, so that I do not have any memory leaks?"
That's the wrong question. The right question is "who should delete these FileData objects?", and the answer is "whoever constructs them, ideally, in this cae Main.cpp". Farming out the job to reportData is awkward and precarious; doing the job twice (once in the ReportData destructor and again in Main.cpp) violates memory.
If you must destroy the objects in ~ReportData(), just don't do anything about them in Main.cpp. Then your code will be correct. Horrible, but correct.
Don't deallocate anything in main().
The destructor for reportData will handle everything allocated with createfd() (just make sure that createfd() is returning what it allocated with new(), since you must not delete anything that was not new'd).
fileData is allocated locally, on the stack, not through new. Since it wasn't allocated by new, don't delete it.
The pointers that were passed into fileData were also passed into reportData, and reportData is responsible for all deletions there. You could check to see that they weren't allocated from an inaccessible memory pool (say in a dynamically linked library); if they were, that's a problem.
So, assuming the deletes are correct in the ReportData destructor, remove any deletion of fileData and you're good.
There is no need to delete or clear either of the two lists - this will be done for you by the default destructor. Assuming that the pointers the lists contain (to "arrays"? I'm not clear) have been dynamically allocated, you need to delete them. However, you can (and should) avoid having to do this explicitly by making the lists contain std::vectors or suitable smart pointers.