Basic questions about RAII, STL pop, and PIMPL
While reading proggit today, I came upon this comment in a post about how the top places in the Google Ai challenge were taken by C++. User reventlov
declares
The biggest problem I have with C++ is that it's waaay too easy to think that you're a "C++ programmer" without really understanding all the thin开发者_StackOverflowgs you need to understand to use C++ acceptably well.
You've got to know RAII, and know to use namespaces, and understand proper exception handling (for example, you should be able to explain why the pop() methods in the STL do not return the values they remove). You've got to know which of the three generations of functions in the standard library is the right one. You should be familiar with concepts like PIMPL. You need to understand how the design of the standard library (especially the STL) works. You need to understand how macros interact with namespaces, and why you usually shouldn't use macros in C++, and what you should use instead (usually templates or inlines, rarely a class). You need to know about boost.
I think I'm one of those clueless C++ programmers he mentions. To keep this brief, my questions are
- Can you give an example of a typical RAII oversight mistake, e.g. where best practices dictate the use of RAII but programmers have implemented using some other way?
- Why doesn't the pop() methods in STL return the value they remove?
- I read the Wikipedia entry for PIMPL, didn't understand any of it. Can you give an example of a typical usage of the PIMPL idiom.
A good example where RAII is crucial but sometimes forgotten is when locking a mutex. If you have a section of code that locks a mutex, performs operations, then unlocks it, if the operations throw an exception or otherwise cause the thread to die, the mutex remains locked. This is why there are several scoped lock classes (like QMutexLocker) since as stated here, you are guaranteed that the destructor will run. So if you use a scoped lock it will always unlock on destruction preventing a dead lock.
Pop returns
void
for the sake of speed: SGI FAQ, and to prevent exceptions that may be thrown by the objects copy constructor.PIMPL is used heavily by the Qt framework to provide binary compatibility. It allows you to hide all the internals of a data structure from the public API. This means, if you want to add private members to a class, you add it to the d-pointer. This maintains Binary Code Compatibility since the only data member exposed is a pointer.
I'll answer point 2, and leave the rest for others. The main reason why pop
doesn't return the removed value is due to exception safety.
First, understand that C++ containers (unlike, say, Java's ones) hold their objects by value. That means that if you want the container to return the object at popping time, it has to return you that object by value, by copying the object being removed. In contrast, by having you access top
before pop
, it can simply return a reference to the top element, and you can copy it to your heart's content before popping it. (Whereas, if pop
returned the element by reference, it'd be a dangling reference, as the object is no longer in the container.)
The consequence of making pop
return by value (aside from the inefficiency involved in copying the object being removed) is that it jeopardises exception safety. Ideally, if an operation throws an exception, the state of the objects involved is unchanged. But if pop
were to return the removed object by value, what if the copy constructor for that object failed? The object would already have been removed from the container, and thus the state is already changed.
This is wordier than I wanted to make it, but hopefully gives you an idea why pop
returning a value is a bad idea.
Read Herb Sutter's "Exceptional C++" and "Exceptional C++ Style".
精彩评论