68

Consider this code:

#include <vector>

void Example()
{
    std::vector<TCHAR*> list;
    TCHAR* pLine = new TCHAR[20];
    list.push_back(pLine);
    list.clear();    // is delete called here?
    // is delete pLine; necessary?
}

Does list.clear() call delete on each element? I.e. do I have to free the memory before / after list.clear()?

Kevin
  • 16,549
  • 8
  • 60
  • 74
Ignas Limanauskas
  • 2,436
  • 7
  • 28
  • 32

6 Answers6

55

std::vector does call the destructor of every element it contains when clear() is called. In your particular case, it destroys the pointer but the objects remain.

Smart pointers are the right way to go, but be careful. auto_ptr cannot be used in std containers. boost::scoped_ptr can't either. boost::shared_ptr can, but it won't work in your case because you don't have a pointer to an object, you are actually using an array. So the solution to your problem is to use boost::shared_array.

But I suggest you use std::basic_string<TCHAR> instead, where you won't have to deal with memory management, while still getting the benefits of working with a string.

Kevin
  • 16,549
  • 8
  • 60
  • 74
Benoît
  • 16,798
  • 8
  • 46
  • 66
  • Good point, feel free to edit these details into my answer... (Me's slightly rusty in me C++ and am embarassed I didnt mention basic_string!) – Ruben Bartelink Feb 27 '09 at 09:46
  • When I comes to memory management, I am somewhat a control freak. I live in the land of embedded (mobile phones) development. It gives me the creeps to use partially managed API. The layer under UI is ANSI C. – Ignas Limanauskas Feb 27 '09 at 10:34
  • What do you consider "partially managed" ? shared_array or basic_string ? Either way, i think it's pretty complete ! If you're into controlling memory management, i would not use std::vector in the first place. It might reserve memory without telling you ! – Benoît Feb 27 '09 at 10:45
  • @Benoit: Oh yeah, well I gave you 10 to head you towards the 3k! @Ignas: Well worth exploring managed stuff and the RAI pattern in general (I've been exploring it in .NET for a few years, but even before that, I'd written my last delete/free many years before!) – Ruben Bartelink Feb 27 '09 at 11:24
  • @Ruben Bartelink : thanks but still a long way to go, i guess ! – Benoît Feb 27 '09 at 13:21
  • @Benoit: by "partially managed" I meant the whole system I am working on. What I am implementing right now (one piece of UI) is some 2% of the whole thing. Some 90% of the solution can never be managed. – Ignas Limanauskas Feb 28 '09 at 04:06
  • @Benoit: I use std::vector just to cut some corners. I need some easy way to have a dynamically changing array without lots of effort. Any recommendations instead of std::vector? – Ignas Limanauskas Feb 28 '09 at 04:09
  • Not really. If you want to make sure that std::vector does not reserve slots unless you ask for them, there might be compilation options for stlport that do that. Not sure though ! – Benoît Feb 28 '09 at 08:12
  • @Benoit: You're right, that'll teach me! [about not not reading the text of the answers before I comment] (Only visited this post as it was +1d yesterday) – Ruben Bartelink Jun 04 '09 at 19:31
42

No (you need to do the delete yourself at the end as you suggest in your example as the destruction of the bald pointer doesnt do anything). But you can use a boost [or other RAII-based idiom] smart pointer to make it Do The Right Thing (auto_ptr would not work correctly in a container as it has incompatible behaviour under copying etc.), but be sure you understand the pitfalls of such smart pointers before use. (As Benoit mentions, in this case, basic_string is what you're really looking for here.)

Having said that there's a need to understand the pitfalls of smart pointers, having them take care of the memory management implicitly so you dont have to do it explicitly is far less error-prone.

EDIT: Substantially revised to encompass the elements Benoit brought into his far more thorough answer, thanks to strong prodding from the Earwicker and James Matta - thanks for pushing me to do the due diligence on this!

Ruben Bartelink
  • 59,778
  • 26
  • 187
  • 249
9

Here's one way that you can tell that it doesn't - try it on a class that's not fully defined:

#include <vector>
class NotDefined;

void clearVector( std::vector<NotDefined*>& clearme )
{
    clearme.clear();    // is delete called here?
}

If this snippet compiles, then it can't be calling the destructor, because the destructor isn't defined.

tfinniga
  • 6,693
  • 3
  • 32
  • 37
  • I think your answer might be true for this specific case but not in general I thought of `SFINAE` or https://en.cppreference.com/w/cpp/types/is_destructible they would allow to call destructor if defined. – Et7f3XIV Feb 11 '23 at 01:17
8

You could just write a simple template function that does this for you:

template <class T>
void deleteInVector(vector<T*>* deleteme) {
    while(!deleteme->empty()) {
        delete deleteme->back();
        deleteme->pop_back();
    }

    delete deleteme;
}

Maybe something in here is bad practice but I don't think so. It looks okay to me though comments are always nice.

Robert Massaioli
  • 13,379
  • 7
  • 57
  • 73
  • 1
    This doesn't really solve the problem. If `list.push_back(pLine)` throws, the memory will still be leaked. The correct thing is to us `std::vector` instead of `TCHAR*`. – Mankarse Jul 01 '14 at 03:49
  • 2
    This is a bad approach. Pointers everywhere make programs extremely slow. The critical way to write fast programs nowadays is to always use contiguous memory, and the best way is to use vector directly, T not being a pointer. – Dom Feb 24 '17 at 13:43
8

Nope. It doesn't do that since there is no guarantee you are not using the pointer anywhere else. If it wasn't a pointer variable, it would free them (by calling the destructor)

Mehrdad Afshari
  • 414,610
  • 91
  • 852
  • 789
  • 8
    @Ignas: How should std::vector know how to delete the pointer? It might have been allocated with new, with malloc or with any other memory allocation function (OS-Specific, hand-written), there's no way to tell from the type. – Niki Feb 27 '09 at 10:10
0

You might also be able to use the Boost Pointer Container Library. Not specifically recommended here (again because you're using arrays instead of single objects, though std::string would take care of that), but it's a useful and little-known library that solves the problem stated in the title.

Head Geek
  • 38,128
  • 22
  • 77
  • 87