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
Related
I am trying to grasp the pointers in C++ but I can't find the answer to these questions.
If I was to have an ArrayList in Java and I wanted to add new Objects to it in a loop I would do something like:
ArrayList<MyObject> list = new ArrayList<MyObject> ();
for (int i = 0; i < otherList.length; i++) {
list.add(i, new MyObject(otherList.get(i)));
}
But let's say I wanted to do the same thing in C++ using vectors. I have found two methods:
vector<MyObject> vector;
for (auto i = otherVector.begin(); i != otherVector.end(); i++) {
// do this
vector[i - otherVector.begin()] = * new MyObject(*i);
// or this
MyObject newObj(*i);
vector[i - otherVector.begin()] = newObj;
}
What is the difference between those 2 methods and if I use the second one, do I need to manually delete the pointers from the list? If I were to use the second method with smart pointers, would they be automatically deleted by the gc when the vector was not used anymore?
The first method creates a memory leak. There's no saving it. Forget you ever heard of operator new.
if I use the second one, do I need to manually delete the pointers from the list?
There are no pointers in the list.
The second would work, and it would clean up after itself when vectorgoes out of scope. But do not do that either.
vector<MyObject> vector;
for (auto i = otherVector.begin(); i != otherVector.end(); i++) {
// do this
vector[i - otherVector.begin()] = * new MyObject(*i); // No, no, no, no.
// or this
MyObject newObj(*i);
vector[i - otherVector.begin()] = newObj; // Please don't.
}
But here's one way it should be done. No loop. (And don't name things "vector".)
std::vector<my_object> vec(other_vector);
If you're really into the brevity thing, do this:
auto vec{other_vector};
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.
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.)
I want to allocate memory for a huge bulk of objects. Then construct them one by one. So I do the following:
BaseClass* buf = static_cast<BaseClass*> (::operator new (sizeof(BaseClass[5])));
for (int var = 0; var < 5; ++var) {
new (&buf[var]) BaseClass(var);
}
And everything seems ok. But when I add delete:
BaseClass* buf = static_cast<BaseClass*> (::operator new (sizeof(BaseClass[5])));
for (int var = 0; var < 5; ++var) {
new (&buf[var]) BaseClass(var);
// ... do something
delete &buf[var];
}
I got "segmentation fault" error. On second iteration (on constructor).
At the same time
delete [] buf;
works fine.
So the question is - why this?
First of all if you use a placement new then you will need to call destructor explicitly
buf[var].~BaseClass();
Then you can delete just things that have been allocated with a new, while &buf[0] works since it's the address returned by the placement new, &buf[1] has not been directly allocated by the memory manager through ::operator new. You can't free them one by one.
So you should do something like
::operator delete(buf);
Placement new doesn't allocate memory, it just calls the constructor for the memory location you gave it. So you don't need to delete the individual items. Instead of
delete &buf[var];
Try this, which calls the destructor without freeing memory:
buf[var].~BaseClass();
Note that you still need to use ::operator delete on the whole chunk of memory, just not on the individual objects.
You're using delete without a corresponding new. Using placement new to construct objects in previously allocated memory does not require a paired use of the delete operator. Instead placement new should be paired with explicit destructor calls. Since you're calling the global operator new function directly, you should be pairing that with a direct call to the global operator delete function, rather than using the delete operator at all.
However none if this is necessary for what you've described. The following is much easier:
std::vector<BaseClass> buf;
buf.reserve(5);
for (int var = 0; var < 5; ++var) {
buf.emplace_back(var);
}
After you have this collection of objects you can put pointers to them into a std::vector<BaseClass*>:
std::vector<BaseClass*> buf2;
buf2.reserve(buf.size());
std::transform(std::begin(buf), std::end(buf), std::back_inserter(buf2),
std::addressof<BaseClass>);
Just be sure not to do anything to the original vector that invalidates the pointers. You can move it, but don't copy it and then destroy the original.
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.