32

Most of my work is done in Python, but I am about to embark into a serious project demanding a lot of C++. Throughout my Python experience, I've found the Zen of Python (presented as an appendix below) to be a serious source of inspiration.

I know (or I think I know) that in order to get somewhere in C++ I need to follow closely the advice in the books "The C++ Programming Language" (how the language was designed) and "Effective C++" (how to actually use it). In addition to those two fine sources of information, is there some sort of "Zen of C++" I could follow?

I am looking for actionable, non-obvious but common-sense pointers on how to be effective.

So far the responses seem to agree on the following (wording needs to be polished).

  • Compile time errors are better than run time errors
  • Prefer references over pointers
  • static_cast is better then dynamic_cast
  • Don't use casting to fix bad design decisions
  • Single inheritance is better than multiple inheritance
  • One class per file is important
  • The object that called new should be the one that frees that memory
  • The user should not suffer any disadvantage because of a language or library feature s/he does not use ("you don't pay for what you don't use")
  • Anything unsafe should be made explicit; the more unsafe, the more explicit.
  • There should generally be a way to do everything that could be reasonably conceived of within the Standard
  • Interoperability with C is a Good Thing
  • Code should be expressive, but not lengthy
  • The language shouldn't force you to use a given paradigm
  • If you can do it in one line don't do it in one line
  • Always know where your things are and what they hold
  • NULL is not so null unless you define it
  • The fact that it is there doesn't mean it should be there
  • Never use something unless you know it's useful
  • void is not necessarily empty
  • classes are better than structs except when they're not
  • Pass function parameters as const references as much as possible
  • RAII - Resource Acquisition Is Initialization
  • If possible, prefer standalone functions to class member functions
  • Use the paradigm that makes sense
  • Use the standard library when possible

I've seen little about "smart pointers", and I think it should be added to the list. From Wikipedia:

In computer science, a smart pointer is an abstract data type that simulates a pointer while providing additional features, such as automatic garbage collection or bounds checking. These additional features are intended to reduce bugs caused by the misuse of pointers while retaining efficiency. Smart pointers typically keep track of the objects they point to for the purpose of memory management.

Of course, certain aspects of the "Zen of Python" also apply to C++.

Appendix: The Zen of Python

  • Beautiful is better than ugly.
  • Explicit is better than implicit.
  • Simple is better than complex.
  • Complex is better than complicated.
  • Flat is better than nested.
  • Sparse is better than dense.
  • Readability counts.
  • Special cases aren't special enough to break the rules.
  • Although practicality beats purity.
  • Errors should never pass silently.
  • Unless explicitly silenced.
  • In the face of ambiguity, refuse the temptation to guess.
  • There should be one-- and preferably only one --obvious way to do it.
  • Although that way may not be obvious at first unless you're Dutch.
  • Now is better than never.
  • Although never is often better than right now.
  • If the implementation is hard to explain, it's a bad idea.
  • If the implementation is easy to explain, it may be a good idea.
  • Namespaces are one honking great idea -- let's do more of those!

For reference, I'm also adding the Zen of Perl. It is interesting to notice the differences with the Zen of Python; understanding language philosophy is important for being effective.

Appendix 2: The Zen of Perl (taken from Perlmonks)

  • Beauty is subjective.
  • Explicit is recommended, but not required.
  • Simple is good, but complex can be good too.
  • And although complicated is bad,
  • Verbose and complicated is worse.
  • Brief is better than long-winded.
  • But readability counts.
  • So use whitespace to enhance readability.
  • Not because you're required to.
  • Practicality always beats purity.
  • In the face of ambiguity, do what I mean.
  • There's more than one way to do it.
  • Although that might not be obvious unless you're a Monk.
  • At your discretion is better than not at all.
  • Although your discretion should be used judiciously.
  • Just because the code looks clean doesn't mean it is good.
  • Just because the code looks messy doesn't mean it is bad.
  • Reuse via CPAN is one honking great idea -- let's do more of that!
37

Your "Zen of C++" is called C++ Coding Standards from Alexandrescu and Sutter (two C++ gurus). Get it. Here are the first 25 guidelines (of 101), which are of course discussed in the book:

Organizational and Policy Issues

  1. Don't sweat the small stuff. (Or: Know what not to standardize)
  2. Compile cleanly at high warning levels.
  3. Use an automated build system.
  4. Use a version control system.
  5. Invest in code reviews.

Design Style

  1. Give one entity one cohesive responsibility.
  2. Correctness, simplicity, and clarity come first.
  3. Know when and how to code for scalability.
  4. Don't optimize prematurely.
  5. Don't pessimize prematurely.
  6. Minimize global and shared data.
  7. Hide information.
  8. Know when and how to code for concurrency.
  9. Ensure resources are owned by objects. Use explicit RAII and smart pointers.

Coding Style

  1. Prefer compile- and link-time errors to run-time errors.
  2. Use const proactively.
  3. Avoid macros.
  4. Avoid magic numbers.
  5. Declare variables as locally as possible.
  6. Always initialize variables.
  7. Avoid long functions. Avoid deep nesting.
  8. Avoid initialization dependencies across compilation units.
  9. Minimize definitional dependencies. Avoid cyclic dependencies.
  10. Make header files self-sufficient.
  11. Always write internal #include guards. Never write external #include guards.

To be continued...

12
  • Compile time errors are better than run time errors
  • Specific Functions are better than Operator overloading
  • Reference are more preferable than pointers
  • Try to avoid the void*
  • Function overloading is better than using default values
  • static_cast is better then dynamic_cast
  • Don't use casting to fix bad design decisions
  • Single inheritance is better than multiple inheritance
  • One class per file is important
  • The object that called new should be the one that frees that memory
9

I'd say the answer is definitely Scott Meyers' Effective book series. These books are a series of tips similar in concept to, though more specific than, the Zen of Python. He thoroughly explains each and every one, and they are almost entirely good advice (there are a few that lose traction with new developments in the language, but if you don't know of those, then they're good advice).

C++ also has some design philosophies which do not affect how you code, but will increase your understanding of 'why does it work like this?'. Stuff in parentheses is my own comment about the design philosphies:

  • The user should not suffer any disadvantage because of a language or library feature e does not use (often referred to as the "you don't pay for what you don't use" policy).
  • Anything unsafe should be made explicit. The more unsafe, the more explicit.
  • There should generally be a way to do everything that could be reasonably conceived of within the Standard (I add the qualifier because people often try to manipulate things that don't even have to exist under the Standard, like vtables)
  • Interoperability with C is a Good Thing.
  • Code should be very expressive, but not lengthy (there are areas of the language where this doesn't work; these are largely considered problems).
  • Correctness is important (look at the type-safety mechanisms like const and real templates), but if you really want to, you can ignore it.
  • The language shouldn't force you to use a given paradigm.
  • It's okay to leave areas unpolished if the polish would conflict with more significant goals (see the first bullet)

Many of the answers on Bjarne Stroustrup's technical FAQ provide insight into the design behind C++.

8

I found this funny, not necessarily zen-like though:

I saw 'cout' being shifted "Hello world" times to the left and stopped right there. ? Steve Gonedes

7

Over the years, C++ has migrated somewhat to a functional-esque language-style, largely due to the influence of STL and later Boost, which both generally avoid object-oriented design in favor of generic, functional-driven patterns and compile-time polymorphism. C++0x is further moving in that direction with built-in lambda support and standardized functional libraries like std::bind. I personally think this is a positive trend, as I never felt OO was C++'s strong suit. STL and Boost have really shown that C++ can be an incredibly elegant, expressive language, without sacrificing raw performance. That's a lot more than can be said for many other modern languages these days.

So basically I think generic, functional-style programming, combined with automatic stack-based resource management (RAII), is really the "Zen of C++."

5
  • Prefer references to pointers (& instead of *). This avoids the need to check for non-nullness over and over again.
  • Pass function parameters as const references as much as possible (except for tiny objects). For example: "const Param& param". This avoids excessive copying while guaranteeing that the value is not changed.
  • Explicit heap allocation (using "new") is almost always avoidable. Instead, declare objects on the stack, or as a part of a class or struct. And when the use of "new" is really necessary, use some smart pointer class (for example "shared_ptr").
  • Avoid using class member functions for everything; when possible, use standalone functions. This improves encapsulation.
4

Doesn't really have one (Python does, C does -- Spirit of C in the Rationale of the ISO Standard -- C++, not so much). But this article comes close...:

there are three basic paradigms of C++ architecture that organize these tricks into a coherent framework.

  • Indirection
  • Homomorphic type hierarchies
  • Memory spaces

Each is supported by specific syntax and features of C++ and the three principles work together to solve an amazing variety of problems. There are other C++ design principles one could add to the list, but these are the core of any advanced C++ design.

I think templates also need to be on the Big List Of Principles -- but, this is a start. Do read the whole article!

4

My zen of C++

  • Complex is better than complicated
  • If you can do it in one line don't do it in one line
  • Always know where your things are and what they hold
  • Don't look at it if you don't want to see it
  • NULL is not so null unless you define it
  • X and ::X might not be the same
  • If you can't do it in 25 lines don't do it
  • The fact that it is there doesn't mean it should be there
  • Never use something unless you know it's useful
  • This is NOT this
  • void is not necessarily empty
  • classes are better than structs except when they're not
3

A few rules of thumb I use a lot:

  • RAII (Resource Acquisition Is Initialization): Let the lifetime of resources be managed by the language by wrapping the raw pointers or resource handles in a stack-based object, which cleans up the resource when it goes out of scope
  • Use the paradigm that makes sense (and understand the ones that are available to you) (C++ is much more than an OOP language. As Charles Salvia mentioned, C++ has moved towards a functional style in recent years, and generic programming has become a Big Deal. I tend to use OOP only on a very small scale, in designing individual classes that I need to use in the larger picture which is designed according to other paradigms)
  • Think about ownership. Because C++ isn't a garbage collected language, someone has to clean up every resource you allocate. RAII, mentioned above is a key ingredient in this, but another part of it is simply thinking. Who owns this resource? Which object or function determines when the resource should be deallocated? If one entity owns the resource, it had better outlive everyone else who use the resource. If an unknown number of objects need to use it, and you can't determine a single one who will outlive all the others, they have shared ownership, so you should probably wrap the resource in a shared_ptr or similar. Should it be possible to transfer ownership? And so on. You need to think about these things.
  • Don't use pointers (As mentioned above, pointers can almost always be replaced by RAII classes of some sort (perhaps predefined smart pointers from some library))

Not very zen-like, perhaps, but I've found it to be useful advice anyway.

2

I would say there is no such thing. C++ can be used in very different manners: procedural ("better C"), with concrete types only but no inheritance (Modula2/Ada style), traditional Simula-style OOP, generic (Boost) or any combination of them. Each usage style would require a different coding standard (I don't like using terms like "zen" here - we are talking about programming languages, not philosophies or religions).

2

Plenty of good answers to this question. My additions are:

  • Always use standard libraries and algorithms, unless it doesn't make sense to do so
  • Although not a "standard" library, Boost is an excellent source for well-thought algorithms and data structures
1

My $0.02:

  • Don't use polymorphism when you really need meta-programming
  • Take the time to consider the type correctness of templates.
0

Why do you think the Zen of Python only applies to python? They just seem like good programming rules of thumb to me (are you planning to write ugly code?).

-1 accepted

This question is highly subjective. Yet, many people submitted their input. Here I provide the condensed version of your input; these guidelines seem to be general enough for keeping the majority happy.

  • Compile time errors are better than run time errors
  • Prefer references over pointers
  • static_cast is better then dynamic_cast
  • Don't use casting to fix bad design decisions
  • Single inheritance is better than multiple inheritance
  • One class per file is important
  • The object that called new should be the one that frees that memory
  • The user should not suffer any disadvantage because of a language or library feature s/he does not use ("you don't pay for what you don't use")
  • Anything unsafe should be made explicit; the more unsafe, the more explicit.
  • There should generally be a way to do everything that could be reasonably conceived of within the Standard
  • Interoperability with C is a Good Thing
  • Code should be expressive, but not lengthy
  • The language shouldn't force you to use a given paradigm
  • If you can do it in one line don't do it in one line
  • Always know where your things are and what they hold
  • NULL is not so null unless you define it
  • The fact that it is there doesn't mean it should be there
  • Never use something unless you know it's useful
  • void is not necessarily empty
  • classes are better than structs except when they're not
  • Pass function parameters as const references as much as possible
  • RAII - Resource Acquisition Is Initialization
  • If possible, prefer standalone functions to class member functions
  • Use the paradigm that makes sense
  • Use the standard library when possible

Additionally, TheSamFrom1984 contributed the following "formal" reference for more detailed C++ programming practices:

Your "Zen of C++" is called C++ Coding Standards from Alexandrescu and Sutter (two C++ gurus).

This reference also seems to be agreed upon by the majority. Thank you all for your comments and participation.