Is there any function that allows me to completely remove and deallocate all contents of a std::map? The functions clear() only removes all elements, it doesn't free any memory.

Recommended Answers

All 12 Replies

It doesn't free any memory as far as I know, is what I meant to say.

Rather than put raw pointers, a common technique is to use shared pointers, which use reference counting. Otherwise need to need to iterate and delete your own objects

You mean besides reducing the scope of the std::map object? You can swap it to an empty temporary container:

int main() {
  std::map<int,int> my_large_map;
  //... allocate a large number of elements in map.
  std::map<int,int>().swap(my_large_map); //swap with an empty temporary
  //at this point my_large_map is empty and the temporary was destroyed.
};

Typically though, you would just reduce the scope:

int main() {
  {
  std::map<int,int> my_large_map;
  //... allocate a large number of elements in map.
  }; // this will destroy the map completely. If you need another map later, just create a new one.
};

However, I have to say that std::map is usually implemented as a self-balancing binary search tree. This is akin to a linked-link type of storage (sequential container), so calling clear() probably does deallocate all the memory in most implementations.

@template<>: I don't think you understood the question.... or I didn't.

What I mean is, I have a global map (probably a bad idea) and I want to explicitly free it's used memory so that my memory leak tracker won't report it. It's reporting it because it dumps any unfreed memory at the end of main, but because it's a global map it doesn't get destructed before that.

I'm pretty sure that swapping with a temporary will solved that problem.

PS: You should use a better memory leak tracker, it sounds like yours sucks. Use Valgrind or something similar.

Real answer: Use valgrind.

Another plausible answer:

Do something like this:

// your global variable, or a thread local variable when appropriate.
std::map<key, value> *my_var = NULL;

template <class T>
class dynamic_bind {
    T saved_;
    T *var_;
 public:
    dynamic_bind(T *var, const T& value) : saved_(*var), var_(var) {
        *var_ = value;
    }
    ~dynamic_bind() {
        *var_ = saved_;
    }
};

// later...
int main() {
    boost::scoped_ptr m(new std::map<key, value>());
    dynamic_bind b(&my_var, m.get());

    ... body of main function ...

    // Before main returns, b's destructor gets called,
    // setting my_var back to NULL, and then m's destructor
    // gets called, freeing the map.
}
commented: -1 for suggesting such a dirty hack... +0

Hello mike_2000_17. Would you mind explaining why that's a dirty hack? That's not dirty at all. It's a proper example of the power of RAII, exception-safe and an excellent tool in general. Global variables (and "dynamic variables," which this construct helps implement) have their place in software and it's better to learn how to deal with them properly than to cover your eyes.

Ok... This might get a bit out of the scope of this thread, but let's do it anyways:
A. Here are some obvious problems with your code:
1) It uses a global variable (ok.. that's not worse than the OP's code).
2) It's a global pointer (which could, obviously point to nowhere).
3) The global pointer is initialized at the start of the main function and thus, puts all the code that executes prior to that into jeopardy. And similarly at clean-up. Global objects should ALWAYS use an initialize-on-first-use idiom.

B. And some coding practices problems:
1) You require all client code to initialize the global pointer with this "unusual" method (unusual in the sense that it is more than a typical new/delete or smart pointer).
2) You require all client code that uses the global pointer to check for nullity in order to remain robust.

C. And finally to the point of the original post:
1) The issue here is to free the memory that is allocated by the std::map object, not freeing the object itself. The object itself is of static storage, so it is not relevant to the problem, it's only its internal dynamic storage that matters (and there are plenty of ways to force that internal storage to get freed without having to devise a scheme that makes the global statically-stored object a global dynamically-stored object pointer that can be freed altogether).

To me, all of the above, together, are enough to merit the name "dirty hack". It's a hack because it is unsafe and brittle, and it's dirty because it dumps a lot of crap on the client-side code.

To address your points:

It's a proper example of the power of RAII

If you mean that dynamic_bind is a RAII class, then I guess you could say it is an example of RAII, I wouldn't really say it's a proper or powerful example of it though.

exception-safe

Well, it would be difficult to make this exception unsafe. And you would probably need to add a couple of "throw()" qualifiers to really make it exception-safe. Anyways, exception safety is hardly a big deal overall (exception-safe != safe or robust).

an excellent tool in general

I can't think of any useful application of this. This piece of code basically creates a global alias for a local variable. How that could ever be a good idea is beyond me. Either you need a local variable (or data member) and should not expose it globally, or you need a global variable in which case you initialize on first use.

Global variables (and "dynamic variables," which this construct helps implement) have their place in software

I agree totally. Although, they are not the most desirable constructs, they are sometimes unavoidable or even appropriate. I'm merely questioning the means to do it. Issuing a shared_ptr out of a global function that constructs it on first use would already be a bit better:

//no global variable (solves A.1) nor global pointer (solves A.2)
boost::shared_ptr< std::map<key,value> >& get_my_var() {
  //initialize on first use (solves A.3)
  static boost::shared_ptr< std::map<key,value> > my_var(new std::map<key,value>());
  return my_var;
};

int main() {
  //no need for initialize (solves B.1).
  
  //if it is required to really delete the global variable before the end (very unlikely):
  get_my_var() = boost::shared_ptr< std::map<key,value> >(); //solves C.1
};

Only problem B.2 remains, and it is pretty much inevitable if you want to retain the "dynamic variable" aspect of it.

and it's better to learn how to deal with them properly than to cover your eyes.

Programming is all about defining problems and finding solutions/idioms/tools to solve them. Here, the problem was to free the storage that an STL container might still have even after its content has been cleared, and doing so without being able to delete the container itself. The swap-to-temporary is an idiom that solves exactly that problem, no fuss, no hassle, no pain. Learning how to deal with global object pointers is something I know all too much about and I don't wish that on anyone. Being able to deal with such constructs is not a skill, it's a symptom of bad coding practices. Finally, my eyes have seen enough C++ horror in the past that this little dirty hack certainly doesn't require covering them.

I only claim that this idiom is usually very acceptable, and sometimes necessary in some situations, because initialize-on-first-use is unacceptable in some situations, for performance and correctness reasons. These are reasons for which you need to do something at start-up, and not initialize a variable later. At the same time, you need the variable to be global, because passing it as a parameter to everything is insane.

A.1 and A.2 are claims supported by other parts of your reply so of course I won't address them directly.

As for A.3, that's correct (except the last sentence), and irrelevant, since it's a hypothetical risk that doesn't really happen. Your pre-construction and post-destruction will fail in an obvious manner under testing. (That's assuming it's not horrible and taking different code paths based on the state of the world outside the process.)

B. And some coding practices problems:
1) You require all client code to initialize the global pointer with this "unusual" method (unusual in the sense that it is more than a typical new/delete or smart pointer).

This is a downside if you have multiple main functions, but unavoidable if initialize-on-first-use is intolerable. You can make this very simple by defining a type global_lifetime_holder so that users just need

int main() {
    global_lifetime_holder foo;
    ...
}

This is not onerous. You can set things up so that an error is reported upon termination if the global_lifetime_holder constructor was never called.

2) You require all client code that uses the global pointer to check for nullity in order to remain robust.

No, you don't. That code will fail in an obvious way under testing. Checking pointers for null values all the time if they're never supposed to be null is a cargo-cult form of robustness. If you don't want it to be null, don't let people assign to it. You could add some bureaucracy around the variable but it won't give you any substantial benefit.

Edit: Worrying that someone might assign a null pointer to the global really is just hysterical. They could just as easily assign an empty std::map, if the thing were not a pointer. If you're worried about somebody misusing a variable, make a wrapper type to only permit acceptable operations.

C. And finally to the point of the original post:
1) The issue here is to free the memory that is allocated by the std::map object, not freeing the object itself. The object itself is of static storage, so it is not relevant to the problem, it's only its internal dynamic storage that matters (and there are plenty of ways to force that internal storage to get freed without having to devise a scheme that makes the global statically-stored object a global dynamically-stored object pointer that can be freed altogether).

To me, all of the above, together, are enough to merit the name "dirty hack". It's a hack because it is unsafe and brittle, and it's dirty because it dumps a lot of crap on the client-side code.

If we know an empty std::map is holding memory, we can't be sure that swapping it with a temporary will do any good. The std::map implementation could just always hold a node, even for newly constructed maps. There's at least one reasonable implementation that would do this. The part of my response that was really about the memory leak checker said to use valgrind, the rest of it was to point out that you can easily manage a global object's lifetime yourself.

Well, it would be difficult to make this exception unsafe. And you would probably need to add a couple of "throw()" qualifiers to really make it exception-safe. Anyways, exception safety is hardly a big deal overall (exception-safe != safe or robust).

I suppose the assignment could throw inside the destructor. In a reasonable codebase that's impossible. You know, throw declarations are obsolete and of course never what you want. One could just make it use T* directly instead of T if you were paranoid about somebody making an.. interesting assignment operator.

I can't think of any useful application of this. This piece of code basically creates a global alias for a local variable. How that could ever be a good idea is beyond me. Either you need a local variable (or data member) and should not expose it globally, or you need a global variable in which case you initialize on first use.

If you're implying that you're too stupid to understand how global variables are sometimes a good idea, I refuse to agree. The whole point of a global variable is so that you don't have to pass a universally used context parameter to every function that exists, everywhere. If you want that, and if initialize-on-first-use is unacceptable for performance reasons or you want an explicit initialization and destruction order or if your initialization depends on external input, you've got to do it in the main function.

I agree totally. Although, they are not the most desirable constructs, they are sometimes unavoidable or even appropriate. I'm merely questioning the means to do it. Issuing a shared_ptr out of a global function that constructs it on first use would already be a bit better:

//no global variable (solves A.1) nor global pointer (solves A.2)
boost::shared_ptr< std::map<key,value> >& get_my_var() {
  //initialize on first use (solves A.3)
  static boost::shared_ptr< std::map<key,value> > my_var(new std::map<key,value>());
  return my_var;
};[

int main() {
  //no need for initialize (solves B.1).
  
  //if it is required to really delete the global variable before the end (very unlikely):
  get_my_var() = boost::shared_ptr< std::map<key,value> >(); //solves C.1
};

There are two problems with that. One, shared_ptr is expensive. Copying them needs cross-core synchronization. Well okay, you could usually just return a raw pointer. The second problem is when initializing on first use is simply unacceptable, for performance reasons or correctness reasons.

For performance reasons, in the cases where you can tolerate an expensive startup but not an expensive spike on early operations after startup.

For correctness reasons, when you want something that could fail to fail immediately, upon startup, rather than the first situation in which you try to use a certain component. You don't want a server to wait until midnight before reporting that it doesn't have permission to open the log file.

There's actually a third reason: when it's the natural thing to do. If startup code relies on command line parameters or needs to access hardware, you'll initialize your globals in the main function. For example, a parameter specifying what logfile to use will be needed to start the logging system, which you certainly will want to be a global variable.

Even worse than global variables is to have global variables that are initialized in a complicated, obtuse fashion. This is a reason to eschew initialize-on-first-use as a general default.

I have a global map (probably a bad idea) and I want to explicitly free it's used memory
The functions clear() only removes all elements, it doesn't free any memory.

Something like this, perhaps:

template < typename KEY, typename DATA, typename CMP_KEYS = std::less<KEY>,
            typename ALLOCATOR = std::allocator< std::pair<const KEY,DATA> > >
struct lean_map : public std::map< KEY, DATA, CMP_KEYS, ALLOCATOR >
{
    void clear() { lean_map::~lean_map() ; new (this) lean_map ; }
};

lean_map< int, int > my_map ;

int main()
{
    // use my_map
    my_map[123] = 456789 ;
    // etc

    my_map.clear() ;
}

The same idea, generalised (for any type with default initialisation):

template < typename T > struct scoped_use : boost::noncopyable
{
    explicit scoped_use( T& obj ) : object(obj) {}
    ~scoped_use()
    {
        void* here = boost::addressof(object) ;
        object.T::~T() ;
        new (here) T() ;
    }
    T& object ;
};

#define PASTE_TOKENS(a,b) a ## b
#define DO_PASTE_TOKENS(a,b) PASTE_TOKENS(a,b)
// C++0X
#define SCOPED_USE(a) scoped_use< decltype(a) > \
      DO_PASTE_TOKENS( _temp_scoped_use_helper_ , __COUNTER__ )(a) ;

std::vector<double> my_seq ;

int main()
{
    {
       SCOPED_USE( my_seq ) ;
       // use my_seq
       my_seq.reserve(1000) ;
       // etc
       std::cout << my_seq.capacity() << '\n' ;
    }
    std::cout << my_seq.capacity() << '\n' ;
}

@Rashakil Fol
I think we have a very different point of view on this matter. Probably because of different experiences on different types of software. We might have to agree to disagree. But here is my point of view on some of your comments:

As for A.3, that's correct (except the last sentence), and irrelevant, since it's a hypothetical risk that doesn't really happen.

It might be hypothetical to you, it's not to me. I often split my code into multiple shared libraries in which case there is a lot of "weird" ordering of initialization of different objects, including merging singletons as well. The problem of not being able to have access to valid global variables at ANY time is a very real problem, there is nothing hypothetical about it.

No, you don't. That code will fail in an obvious way under testing. Checking pointers for null values all the time if they're never supposed to be null is a cargo-cult form of robustness. If you don't want it to be null, don't let people assign to it. You could add some bureaucracy around the variable but it won't give you any substantial benefit.

Edit: Worrying that someone might assign a null pointer to the global really is just hysterical. They could just as easily assign an empty std::map, if the thing were not a pointer. If you're worried about somebody misusing a variable, make a wrapper type to only permit acceptable operations.

I agree that testing for nullity is wrong when it is much easier to force the validity of the pointer at all times (that's in part what initialize-on-first-use does, and const-correctness can do the rest). However, I don't agree that "the code will fail in an obvious way under testing" is a good thing. First, the failure might be obvious to you because you wrote the code (the global variable part), but how could it possibly be obvious to the user, beats me. Second, I prefer things that fail at compile-time or don't fail at all. If you allow an operation to compile, it should work at run-time. In other words, if you allow access to a global variable you also take on the contract of making it valid at all times. This way, if code that accesses the global variable is compilable, it means that it will behave as expected at run-time. This can tremendously increase the effectiveness of the programming. You should never force the user to make crazy workarounds after getting unexpected (but "obvious"?) errors during testing because he expected your global variable to be global and not an alias to a local variable. That's just my opinion.

it was to point out that you can easily manage a global object's lifetime yourself.

And my point is that even if it is easy to manage a global object's lifetime, it shouldn't be done. Even just the concept of "manage a global object's lifetime" is semantically broken. To me, a global object has global scope, right? Global scope means the variable's lifetime is that of the application, right? So you can't really manage its lifetime in a conventional sense. You can initialize and finalize it at any time you like (as in vijayan121's answers for example). But constructing and destructing it at any-time is just playing a dirty trick on anyone using your library.

If you're implying that you're too stupid to understand how global variables are sometimes a good idea, I refuse to agree.

My problem is not with global variables, I agree that they are useful in some situations and I have certainly used them in the past. My problem is with masquerading a local variable as a global one. IMO, it is dangerous and unmaintainable. If a variable is global it should be valid at all times, not just during a local scope (even if that local scope is the main() function).

There are two problems with that. One, shared_ptr is expensive. Copying them needs cross-core synchronization.

Really? The overhead of using a shared_ptr is so ridiculously small (especially if it is pointing to a fairly complex construct like std::map) that I can't believe you are seriously saying that. The safety that the reference counting gives you in this context is far more valuable than any tiny performance penalty you might get from the copying. At least, that's my opinion, and I have used shared pointers a lot for that reason and it has made things dramatically better in terms of robustness AND performance (because it avoids expensive workarounds to deal with raw pointers in a safe manner).

The second problem is when initializing on first use is simply unacceptable, for performance reasons or correctness reasons.

For performance reasons, in the cases where you can tolerate an expensive startup but not an expensive spike on early operations after startup.

You do understand that the initialize-on-first-use does not exclude, in any way, the possibility of initializing on startup. You can either make a dummy use of it at startup, just to ensure that it is initialized before entering any time-critical process. Or, you can provide an additional initialization function, just to make it more obvious.

For correctness reasons, when you want something that could fail to fail immediately, upon startup, rather than the first situation in which you try to use a certain component. You don't want a server to wait until midnight before reporting that it doesn't have permission to open the log file.

Idem.

There's actually a third reason: when it's the natural thing to do. If startup code relies on command line parameters or needs to access hardware, you'll initialize your globals in the main function. For example, a parameter specifying what logfile to use will be needed to start the logging system, which you certainly will want to be a global variable.

Imagine this: You have a global logging object and you have another global object that is part of your software. What if the other global object has something to report or log during its construction. It would, obviously, access the global logger and log that entry. Now, either the logger is not created yet and the application either fails or the log entry is simply not logged and lost. Or, a dummy logger object exists, logs the entry, but then gets replaced later (at startup) by the real logger object (which is initialized to a particular log-file name). So, in either case, you either crash you application for a very stupid reason (the constructor of a global object just wanted to log something, why does that have to crash your software!?!), or you lose information and your logger is basically failing to do its job.

The solution is simple: make your global object a global object. If you make one truly global logger object, it will always be valid and will not lose information. Now, if you want to specify the log-file at startup, then that's trivial. Just make the logger, after default construction, not associated with any file (and in that state, it simply buffers the log entries), and, at startup, you give it a filename which will then cause it to dump the log entries that it has into the file and log the subsequent entries to that file (probably via a buffer). This way, by fulfilling the contract that any global object should be valid at all times, you automatically solve the problem: no crash, no lost information. If a global object is what you need, good chances are, it will be best if it's lifetime is global, as it should. And, in this case, you need initialize-on-first-use.

You could complain that creating a logger that is not associated with a file and later associating it with a file is not good RAII style. Sure, it's not. So what. RAII is very good, very often, but not always. If you need to turn a global variable into an alias of a local variable just so that you can remain in good RAII style, that's just putting your priorities at the wrong places IMO.

Even worse than global variables is to have global variables that are initialized in a complicated, obtuse fashion. This is a reason to eschew initialize-on-first-use as a general default.

Ok, that's just ranting on your part, calm down. Take a deep breath and you will realize how silly it is to imply that initialize-on-first-use is "complicated and obtuse".

Some of your complaints are based around the practice of having other global variables that do fancy initialization upon startup. I just avoid these altogether. The initialization of global state is a straightforward process driven by the main function, except for a few standalone hacks. I would want to avoid having a logger with two separate modes of operation. That's more complicated, and more likely to break in non-obvious ways, instead of obvious ones where some variable is null. (Also, if you have to initialize a logger in the main function anyway, we're talking about a more obscure kind of failure if you forget to do that than one which crashes immediately.)

Also, the errors of not initializing a global pointer are obvious. You get a segfault and the stack trace points right at the variable, and it's obvious. This kind of error basically never happens and when it does it's immediately shown on every test and fixed. Your complaints that having a global variable point to a local variable (or a pointer allocated and destroyed in main) is weird are basically irrelevant. It's not weird, we do it all the time. (Well, we do it a couple times, because there aren't that many global variables.) It hasn't led to safety or maintainability problems.

As for the expense of a shared_ptr: it's not small enough for me (or my employer's customers). It of course depends on how much you're copying it, versus how much you're using the map. A std::map is something we would avoid, too. I have to admit my reaction to shared_ptr is somewhat allergic and specific to the kind of situations I'm in, because we have other reasons we need to carefully manage objects' lifetimes for correctness, and the only reason to use shared_ptr is when you're making things complicated and hard to reason about. But that does not apply here.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.