A blast from the past More Effective C++ by Scott Meyers is not just another C++ book, but touches on a number of features and concepts that should be talked about far more than you get from most C++ books.
Some of the topics include: smart pointers, lazy evaluation, preventing resource leaks in constructors, preventing exceptions from leaving destructors, understanding the cost of exception handling, understanding the origin of temporary objects, understanding the cost of virtual functions, base classes and RTTI and more.
One part I especially liked was avoiding gratuitous default constructors. This is more of a design decision, but still a useful one, in that if member functions have to test if fields have truly been initialized it complicates the design and can lead to bugs and more complicated behaviour in handling these uninitialized classes.
Meyers talks about a number of design decisions, and side effects of going too far with overloading methods, or operator new or operator delete (and the numerous hacks and design decisions that have to be considered if you do decide to handle your own new and delete (i.e. don’t do it)).
The chapter on exceptions was particularly intresting, and is probably my favorite description of exceptions:
The addition of exceptions to C++ changes things. Profoundly. Radically. Possibly uncomfortably. The use of raw, unadorned poniters, for example, becomes risky. Opportunities for resource leaks increase in number. It becomes more difficult to write constructors and destructors that behave the way we want them to. Special care must be taken to prevent program execution from abruptly halting. Executables and libraries typically increase in size and decrease in speed… so why mess with exceptions, especially if they are problematic as I say? The answer is simple: exceptions cannot be ignored (pg 44).
When thinking about exceptions in this way (as something that cannot be ignored), it is a great mindset for the programmer to consider what they really consider unignorable. This is quite a bit different than return codes, or status variables, which can be ignored, although at the risk of potentially undefined behaviour. Exceptions are best used when something exceptional happens. For performance reasons, it is best to limit the usage of exceptions whenever appropriate (and exceptional).
There are also some good practical advice, such as program in the future tense. Consider how the code will grow in the future, or how it will be used. Provide complete classes, even if some parts aren’t currently used. Make the class easy to use, and hard to use incorrectly. Generalize the code whenever appropriate to maximize reuse. This is not to say design and program things you aren’t going to need, but rather create logically complete classes (if something can be set, it probably should also have a get, and if an entry can be added, it probably needs a way to remove that entry too).
The book even though it is not hot off the presses, still offers quite a bit of useful insight into C++ and fills a role that few books really delve into. If corners of your C++ knowledge are growing some cobwebs, this is a useful book to help dust some of them off.