PART 13 Q81: What are some good C++ coding standards? A: Thank you for reading this answer rather than just trying to set your own coding standards. But please don't ask this question on Usenet. Nearly every software engineer has, at some point, felt that coding standards are or can be used as a `power play'. Furthermore some attempts to set C++ coding standards have been made by those unfamiliar with the language and/or paradigm, so the standards end up being based on what *was* the state-of-the-art when the setters where writing code. Such impositions generate an attitude of mistrust for coding standards. Obviously anyone who asks this question on Usenet wants to be trained so they *don't* run off on their own ignorance, but nonetheless the answers tend to generate more heat than light. Q82: Are coding standards necessary? sufficient? A: Coding standards do not make non OO programmers into OO programmers. Only training and experience do that. If they have merit, it is that coding standards discourage the petty fragmentation that occurs when organizations coordinate the activities of diverse groups of programmers. But you really want more than a coding standard. The structure provided by coding standards gives neophytes one less degree of freedom to worry about, however pragmatics go well beyond pretty-printing standards. We actually need a consistent *philosophy* of implementation. Ex: strong or weak typing? references or ptrs in our interface? stream I/O or stdio? should C++ code call our C? vise versa? should we use ABCs? polymorphism? inheritance? classes? encapsulation? how should we handle exceptions? etc. Therefore what is needed is a `pseudo standard' for detailed *design*. How can we get this? I recommend a two-pronged approach: training and libraries. Training provides `intense instruction', and a high quality C++ class library provides `long term instruction'. There is a thriving commercial market for both kinds of `training'. Advice by organizations who have been through the mill is consistent: Buy, Don't Build. Buy libraries, buy training, buy tools. Companies who have attempted to become a self-taught tool-shop as well as an application/system shop have found success difficult. Few argue that coding standards are `ideal', or even `good', however many feel that they're necessary in the kind of organizations/situations described above. The following questions provide some basic guidance in conventions and styles. Q83: Should our organization determine coding standards from our C experience? A: No matter how vast your C experience, no matter how advanced your C expertise, being a good C programmer does not make you a good C++ programmer. C programmers must learn to use the `++' part of `C++', or the results will be lackluster. People who want the `promise' of OOP, but who fail to put the `OO' into OOP, are fooling themselves, and the balance sheet will show their folly. C++ coding standards should be tempered by C++ experts. Asking comp.lang.c++ is a start (but don't use the term `coding standard' in the question; instead simply say, `what are the pros and cons of this technique?'). Seek out experts who can help guide you away from pitfalls. Get training. Buy libraries and see if `good' libraries pass your coding standards. Do *not* set standards by yourself unless you have considerable experience in C++. Having no standard is better than having a bad standard, since improper `official' positions `harden' bad brain traces. There is a thriving market for both C++ training and libraries from which to pool expertise. One more thing: any time demand for something is high, the potential for charlatans increases. Look before you leap. Also ask for student-reviews from past companies, since not even expertise makes someone a good communicator. Finally, select a practitioner who can teach, not a full time teacher who has a passing knowledge of the language/paradigm. Q84: Should I declare locals in the middle of a fn or at the top? A: Different people have different opinions about coding standards. However one thing we all should agree on is this: no style guide should impose undue performance penalties. The real reason C++ allows objects to be created anywhere in the block is not style, but performance. An object is initialized (constructed) the moment it is declared. If you don't have enough information to initialize an object until half way down the fn, you can either initialize it to an `empty' value at the top then `assign' it later, or initialize it correctly half way down the fn. It doesn't take much imagination to see that it's cheaper to get it right the first time than it is to build it once, tear it down, then rebuild it again. Simple examples show a factor of 350% speed hit for simple classes like String. Your mileage may vary; surely the overall system degradation will be less that 300+%, but there *will* be degradation. *Unnecessary* degradation. A common retort to the above is: `we'll provide "set" methods for every datum in our objects, so the cost of construction will be spread out'. This is worse than the performance overhead, since now you're introducing a maintenance nightmare. Providing `set' methods for every datum is tantamount to public data. You've exposed your implementation technique to the world. The only thing you've hidden is the physical *names* of your subobjects, but the fact that you're using a List and a String and a float (for example) is open for all to see. Maintenance generally consumes far more resources than run-time CPU. Conclusion: in general, locals should be declared near their first use. Sorry that this isn't `familiar' to your C experts, but `new' doesn't necessarily mean `bad'. Q85: What source-file-name convention is best? `foo.C'? `foo.cc'? `foo.cpp'? A: Most Un*x compilers accept `.C' for C++ source files, g++ preferring `.cc', and cfront also accepting `.c'. Most DOS and OS/2 compilers require `.cpp' since DOS filesystems aren't case sensitive. Some also advocate `.cxx'. The impact of this decision is not great, since a trivial shell script can rename all .cc files into .C files. The only files that would have to be modified are the Makefiles, which is a very small piece of your maintenance costs. Note however that some versions of cfront accept a limited set of suffixes (ie: some can't handle `.cc'; in these cases it is easier to tell `make' about CC's convention than vise versa). You can use `.C' on DOS or OS/2 if the compiler provides a command-line option to tell it to always compile with C++ rules (ex: `ztc -cpp foo.C' for Zortech, `bcc -P foo.C' for Borland, etc). Q86: What header-file-name convention is best? `foo.H'? `foo.hh'? `foo.hpp'? A: The naming of your source files is cheap since it doesn't affect your source code. Your substantial investment is your source code. Therefore the names of your header files must be chosen with much greater care. The preprocessor will accept whatever name you give it in the #include line, but whatever you choose, you will want to plan on sticking with it for a long time, since it is more expensive to change (though certainly not as difficult as, say, porting to a new language). Almost all vendors ship their C++ header files using a `.h' extension, which means you can reliably do things like: #includeWell, *almost*. There are one or two vendors that provide . A few #ifdef's, suffice. There are also tools that recognize the language of a file by its extension rather than its contents (gnu emacs looks in the first few lines for a string magic token in a comment identifying the language). Some of these extension-based tools use `.hh' or `.H' to identify C++ headers, however most of the world is leaning toward `.h' for C++ headers. C++ specific information in a `.h' that is to be shared with a C compiler can be #ifdef'd using: #ifdef __cplusplus /*all C++ compilers define __cplusplus*/ // ... C++ specific stuff here ... #endif Q87: Are there any lint-like guidelines for C++? A: Yes, there are some practices which are generally considered dangerous. However none of these are universally `bad', since situations arise when even the worst of these is needed: * a class `X's assignment operator should return `*this' as an `X&' (allows chaining of assignments) * a class with any virtual fns ought to have a virtual destructor * a class with any of {dtor, assignment-op, copy-ctor} generally needs all 3 * a class `X's copy-ctor and assignment-op should have `const' in the param: `X::X(const X&)' and `X& X::operator=(const X&)' respectively * always use initialization lists for class sub-objects rather than assignment the performance difference for user-defined classes can be substantial (3x!) * many assignment operators should start by testing if `we' are `them'; ex: X& X::operator=(const X& x) { if (this == &x) return *this; //...normal assignment duties... return *this; } sometimes there is no need to check, but these situations generally correspond to when there's no need for an explicit user-specified assignment op (as opposed to a compiler-synthesized assignment-op). * in classes that define both `+=', `+' and `=', `a+=b' and `a=a+b' should generally do the same thing; ditto for the other identities of builtin types (ex: a+=1 and ++a; p[i] and *(p+i); etc). This can be enforced by writing the binary ops using the `op=' forms; ex: X operator+(const X& a, const X& b) { X ans = a; ans += b; return ans; } This way the `constructive' binary ops don't even need to be friends. But it is sometimes possible to more efficiently implement common ops (ex: if class `X' is actually `String', and `+=' has to reallocate/copy string memory, it may be better to know the eventual length from the beginning). See Stroustrup/C++ Programming Language/Second Edition/`Rules of Thumb'/p.10