[Comp.Sci.Dept, Utrecht] Note from archiver<at>cs.uu.nl: This page is part of a big collection of Usenet postings, archived here for your convenience. For matters concerning the content of this page, please contact its author(s); use the source, if all else fails. For matters concerning the archive as a whole, please refer to the archive description or contact the archiver.

Subject: C++ FAQ (part 11 of 14)

This article was archived around: NNTP-Posting-Mon, 17 Jun 2002 22:47:04 EDT

All FAQs in Directory: C++-faq
All FAQs posted in: comp.lang.c++, alt.comp.lang.learn.c-c++
Source: Usenet Version


Archive-name: C++-faq/part11 Posting-Frequency: monthly Last-modified: Jun 17, 2002 URL: http://www.parashift.com/c++-faq-lite/
AUTHOR: Marshall Cline / cline@parashift.com / 972-931-9470 COPYRIGHT: This posting is part of "C++ FAQ Lite." The entire "C++ FAQ Lite" document is Copyright(C)1991-2002 Marshall Cline, Ph.D., cline@parashift.com. All rights reserved. Copying is permitted only under designated situations. For details, see section [1]. NO WARRANTY: THIS WORK IS PROVIDED ON AN "AS IS" BASIS. THE AUTHOR PROVIDES NO WARRANTY WHATSOEVER, EITHER EXPRESS OR IMPLIED, REGARDING THE WORK, INCLUDING WARRANTIES WITH RESPECT TO ITS MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE. C++-FAQ-Lite != C++-FAQ-Book: This document, C++ FAQ Lite, is not the same as the C++ FAQ Book. The book (C++ FAQs, Cline and Lomow, Addison-Wesley) is 500% larger than this document, and is available in bookstores. For details, see section [3]. ============================================================================== SECTION [25]: Built-in / intrinsic / primitive data types [25.1] Can sizeof(char) be 2 on some machines? For example, what about double-byte characters? [NEW!] [Recently created inspired by Randy Sherman (in 5/02).] No, sizeof(char) is always 1. Always. It is never 2. Never, never, never. Even if you think of a "character" as a multi-byte thingy, char is not. sizeof(char) is always exactly 1. No exceptions, ever. Look, I know this is going to hurt your head, so please, please just read the next few FAQs in sequence and hopefully the pain will go away by sometime next week. ============================================================================== [25.2] What are the units of sizeof? [NEW!] [Recently created inspired by Randy Sherman (in 5/02).] Bytes. For example, if sizeof(Fred) is 8, the distance between two Fred objects in an array of Freds will be exactly 8 bytes. As another example, this means sizeof(char) is one[25.1] byte. That's right: one byte. One, one, one, exactly one byte, always one byte. Never two bytes. No exceptions. ============================================================================== [25.3] Whoa, but what about machines or compilers that support multibyte characters. Are you saying that a "character" and a char might be different?!? [NEW!] [Recently created inspired by Randy Sherman (in 5/02).] Yes that's right: the thing commonly referred to as a "character" might be different from the thing C++ calls a char. I'm really sorry if that hurts, but believe me, it's better to get all the pain over with at once. Take a deep breath and repeat after me: "character and char might be different." There, doesn't that feel better? No? Well keep reading -- it gets worse. ============================================================================== [25.4] But, but, but what about machines where a char has more than 8 bits? Surely you're not saying a C++ byte might have more than 8 bits, are you?!? [NEW!] [Recently created thanks to Randy Sherman (in 5/02) and removed a superfluous statement thanks to Gennaro Prota (in 6/02).] Yep, that's right: a C++ byte might have more than 8 bits. The C++ language guarantees a byte must always have at least 8 bits. But there are implementations of C++ that have more than 8 bits per byte. ============================================================================== [25.5] Okay, I could imagine a machine with 9-bit bytes. But surely not 16-bit bytes or 32-bit bytes, right? [NEW!] [Recently created thanks to Randy Sherman (in 5/02).] Wrong. I have heard of one implementation of C++ that has 64-bit "bytes." You read that right: a byte on that implementation has 64 bits. 64 bits per byte. 64. As in 8 times 8. And yes, you're right, combining with the above[25.1] would mean that a char on that implementation would have 64 bits. ============================================================================== [25.6] I'm sooooo confused. Would you please go over the rules about bytes, chars, and characters one more time? [NEW!] [Recently created thanks to Randy Sherman (in 5/02) and rewrote; changed the example from the mythical FOO machine to a PDP-10, plus added words showing how to simulate pointers using software, much thanks to Andrew Koenig (in 6/02).] Here are the rules: * The C++ language gives the programmer the impression that memory is laid out as a sequence of something C++ calls "bytes." * Each of these things that the C++ language calls a byte has at least 8 bits, but might have more than 8 bits. * The C++ language guarantees that a char* (char pointers) can address individual bytes. * The C++ language guarantees there are no bits between two bytes. This means every bit in memory is part of a byte. If you grind your way through memory via a char*, you will be able to see every bit. * The C++ language guarantees there are no bits that are part of two distinct bytes. This means if you change a particular byte, it will not effect the value of any other byte in the system. * The C++ language gives you a way to find out how many bits are in a byte in your particular implementation: include the header <climits>, then the actual number of bits per byte will be given by the CHAR_BIT macro. Let's work an example to illustrate these rules. The PDP-10 has 36-bit words with no hardware facility to address anything within one of those words. That means a pointer can point only at things on a 36-bit boundary: it is not possible for a pointer to point 8 bits to the right of where some other pointer points. One way to abide by all the above rules is for a PDP-10 C++ compiler to define a "byte" as 36 bits. Another valid approach would be to define a "byte" as 9 bits, and simulate a char* by two words of memory: the first could point to the 36-bit word, the second could be a bit-offset within that word. In that case, the C++ compiler would need to add extra instructions when compiling code using char* pointers. For example, the code generated for *p = 'x' might read the word into a register, then use bit-masks and bit-shifts to change the appopriate 9-bit byte within that word. An int* could still be implemented as a single hardware pointer, since C++ allows sizeof(char*) != sizeof(int*). Using the same logic, it would also be possible to define a PDP-10 C++ "byte" as 12-bits or 18-bits. However the above technique wouldn't allow us to define a PDP-10 C++ "byte" as 8-bits, since 8*4 is 32, meaning every 4th byte we would skip 4 bits. A more complicated approach could be used for those 4 bits, e.g., by packing nine bytes (of 8-bits each) into two adjacent 36-bit words. The important point here is that memcpy() has to be able to see every bit of memory: there can't be any bits between two adjacent bytes. Note: one of the popular non-C/C++ approaches on the PDP-10 was to pack 5 bytes (of 7-bits each) into each 36-bit word. However this won't work in C or C++ since 5*7 = 35, meaning using char*s to walk through memory would "skip" a bit every fifth byte (and also because C++ requires bytes to have at least 8 bits). ============================================================================== [25.7] What is a "POD type"? [NEW!] [Recently created (in 5/02) and added note that POD types can contain non-virtual member functions thanks to Andrew Koenig (in 6/02).] A type that consists of nothing but Plain Old Data. A POD type is a C++ type that has an equialent in C, and that uses the same rules as C uses for initialization, copying, layout, and addressing. As an example, the C declaration "struct Fred x;" does not initialize the members of the Fred variable x. To make this same behavior happen in C++, Fred would need to not have any constructors. Similarly to make the C++ version of copying the same as the C version, the C++ Fred must not have overloaded the assignment operator. To make sure the other rules match, the C++ version must not have virtual functions, base classes, non-static members that are private or protected, or a destructor. It can, however, have static data members, static member functions, and non-static non-virtual member functions. The actual definition of a POD type is recursive and gets a little gnarly. Here's a slightly simplified definition of POD: a POD type's non-static data members must be public and can be of any of these types: bool, any numeric type including the various char variants, any enumeration type, any data-pointer type (that is, any type convertible to void*), any pointer-to-function type, or any POD type, including arrays of any of these. Note: data-pointers and pointers-to-function are okay, but pointers-to-member[32] are not. Also note that references are not allowed. In addition, a POD type can't have constructors, virtual functions, base classes, or an overloaded assignment operator. ============================================================================== [25.8] When initializing non-static data members of built-in / intrinsic / primitive types, should I use the "initialization list" or assignment? [NEW!] [Recently created (in 5/02) and clarified the cross-reference (in 6/02).] For symmetry, it is usually best to initialize all non-static data members in the constructor's "initialization list," even those that are of a built-in / intrinsic / primitive type. The FAQ shows you why and how[10.6]. ============================================================================== [25.9] When initializing static data members of built-in / intrinsic / primitive types, should I worry about the "static initialization order fiasco"? [NEW!] [Recently created (in 5/02) and made it clearer that the FAQ provides a solution (in 6/02).] Yes, if you initialize your built-in / intrinsic / primitive variable is initialized by something other than a value that can be computed solely at compile-time. The FAQ provides several solutions[10.15] for this (subtle!) problem. ============================================================================== [25.10] Can I define an operator overload that works with built-in / intrinsic / primitive types? [NEW!] [Recently created (in 5/02).] No, the C++ language requires that your operator overloads take at least one operand of a "class type." The C++ language will not let you define an operator all of whose operands / parameters are of primitive types. For example, you can't define an operator== that takes two char*s and uses string comparison[13.6]. That's good news because if s1 and s2 are of type char*, the expression s1 == s2 already has a well defined meaning: it compares the two pointers, not the two strings pointed to by those pointers. You shouldn't use pointers anyway. Use std::string instead of char*.[17.5] If C++ let you redefine the meaning of operators on built-in types, you wouldn't ever know what 1 + 1 is: it would depend on which headers got included and whether one of those headers redefined addition to mean, for example, subtraction. ============================================================================== [25.11] When I delete an array of some built-in / intrinsic / primitive type, why can't I just say delete a instead of delete[] a? [NEW!] [Recently created (in 5/02).] Because you can't. Look, please don't write me an email asking me why C++ is what it is. It just is. If you really want a rationale, buy Bjarne Stroustrup's excellent book, "Design and Evolution of C++" (Addison-Wesley publishers). But if your real goal is to write some code, don't waste too much time figuring out why C++ has these rules, and instead just abide by its rules. So here's the rule: if a points to an array of thingies that was allocated via new T[n], then you must, must, must[16.12] delete it via delete[] a. Even if the elements in the array are built-in types. Even if they're of type char or int or void*. Even if you don't understand why. ============================================================================== [25.12] How can I tell if an integer is a power of two without looping? [NEW!] [Recently created thanks to S.K. Mody (in 6/02).] inline bool isPowerOf2(int i) { return i > 0 && (i & (i - 1)) == 0; } ============================================================================== SECTION [26]: Coding standards [26.1] What are some good C++ coding standards? Thank you for reading this answer rather than just trying to set your own coding standards. But beware that some people on comp.lang.c++ are very sensitive on this issue. Nearly every software engineer has, at some point, been exploited by someone who used coding standards as a "power play." Furthermore some attempts to set C++ coding standards have been made by those who didn't know what they were talking about, so the standards end up being based on what was the state-of-the-art when the standards setters were writing code. Such impositions generate an attitude of mistrust for coding standards. Obviously anyone who asks this question wants to be trained so they don't run off on their own ignorance, but nonetheless posting a question such as this one to comp.lang.c++ tends to generate more heat than light. ============================================================================== [26.2] Are coding standards necessary? Are they sufficient? Coding standards do not make non-OO programmers into OO programmers; only training and experience do that. If coding standards have merit, it is that they discourage the petty fragmentation that occurs when large 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, which is good. However pragmatic guidelines should go well beyond pretty-printing standards. Organizations need a consistent philosophy of design and implementation. E.g., strong or weak typing? references or pointers in interfaces? stream I/O or stdio? should C++ code call C code? vice versa? how should ABCs[22.3] be used? should inheritance be used as an implementation technique or as a specification technique? what testing strategy should be employed? inspection strategy? should interfaces uniformly have a get() and/or set() member function for each data member? should interfaces be designed from the outside-in or the inside-out? should errors be handled by try/catch/throw or by return codes? etc. What is needed is a "pseudo standard" for detailed design. I recommend a three-pronged approach to achieving this standardization: training, mentoring[27.1], and libraries. Training provides "intense instruction," mentoring allows OO to be caught rather than just taught, and high quality C++ class libraries provide "long term instruction." There is a thriving commercial market for all three kinds of "training." Advice by organizations who have been through the mill is consistent: Buy, Don't Build. Buy libraries, buy training, buy tools, buy consulting. 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 they are necessary in the kind of organizations/situations described above. The following FAQs provide some basic guidance in conventions and styles. ============================================================================== [26.3] Should our organization determine coding standards from our C experience? No! 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. Converting from C to C++ is more than just learning the syntax and semantics of the ++ part of C++. Organizations who want the promise of OO, but who fail to put the "OO" into "OO programming", are fooling themselves; the balance sheet will show their folly. C++ coding standards should be tempered by C++ experts. Asking comp.lang.c++ is a start. 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 pull expertise. One more thing: whenever something is in demand, 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. ============================================================================== [26.4] What's the difference between <xxx> and <xxx.h> headers? The headers in ISO Standard C++ don't have a .h suffix. This is something the standards committee changed from former practice. The details are different between headers that existed in C and those that are specific to C++. The C++ standard library is guaranteed to have 18 standard headers from the C language. These headers come in two standard flavors, <cxxx> and <xxx.h> (where xxx is the basename of the header, such as stdio, stdlib, etc). These two flavors are identical except the <cxxx> versions provide their declarations in the std namespace only, and the <xxx.h> versions make them available both in std namespace and in the global namespace. The committee did it this way so that existing C code could continue to be compiled in C++. However the <xxx.h> versions are deprecated, meaning they are standard now but might not be part of the standard in future revisions. (See clause D.5 of the ISO C++ standard[6.12].) The C++ standard library is also guaranteed to have 32 additional standard headers that have no direct counterparts in C, such as <iostream>, <string>, and <new>. You may see things like #include <iostream.h> and so on in old code, and some compiler vendors offer .h versions for that reason. But be careful: the .h versions, if available, may differ from the standard versions. And if you compile some units of a program with, for example, <iostream> and others with <iostream.h>, the program may not work. For new projects, use only the <xxx> headers, not the <xxx.h> headers. When modifying or extending existing code that uses the old header names, you should probably follow the practice in that code unless there's some important reason to switch to the standard headers (such as a facility available in standard <iostream> that was not available in the vendor's <iostream.h>). If you need to standardize existing code, make sure to change all C++ headers in all program units including external libraries that get linked in to the final executable. All of this affects the standard headers only. You're free to name your own headers anything you like; see [26.8]. ============================================================================== [26.5] Is the ?: operator evil since it can be used to create unreadable code? No, but as always, remember that readability is one of the most important things. Some people feel the ?: ternary operator should be avoided because they find it confusing at times compared to the good old if statement. In many cases ?: tends to make your code more difficult to read (and therefore you should replace those usages of ?: with if statements), but there are times when the ?: operator is clearer since it can emphasize what's really happening, rather than the fact that there's an if in there somewhere. Let's start with a really simple case. Suppose you need to print the result of a function call. In that case you should put the real goal (printing) at the beginning of the line, and bury the function call within the line since it's relatively incidental (this left-right thing is based on the intuitive notion that most developers think the first thing on a line is the most important thing): // Preferred (emphasizes the major goal -- printing): std::cout << funct(); // Not as good (emphasizes the minor goal -- a function call): functAndPrintOn(std::cout); Now let's extend this idea to the ?: operator. Suppose your real goal is to print something, but you need to do some incidental decision logic to figure out what should be printed. Since the printing is the most important thing conceptually, we prefer to put it first on the line, and we prefer to bury the incidental decision logic. In the example code below, variable n represents the number of senders of a message; the message itself is being printed to std::cout: int n = /*...*/; // number of senders // Preferred (emphasizes the major goal -- printing): std::cout << "Please get back to " << (n==1 ? "me" : "us") << " soon!\n"; // Not as good (emphasizes the minor goal -- a decision): std::cout << "Please get back to "; if (n == 1) std::cout << "me"; else std::cout << "us"; std::cout << " soon!\n"; All that being said, you can get pretty outrageous and unreadable code ("write only code") using various combinations of ?:, &&, ||, etc. For example, // Preferred (obvious meaning): if (f()) g(); // Not as good (harder to understand): f() && g(); Personally I think the explicit if example is clearer since it emphasizes the major thing that's going on (a decision based on the result of calling f()) rather than the minor thing (calling f()). In other words, the use of if here is good for precisely the same reason that it was bad above: we want to major on the majors and minor on the minors. In any event, don't forget that readability is the goal (at least it's one of the goals). Your goal should not be to avoid certain syntactic constructs such as ?: or && or || or if -- or even goto. If you sink to the level of a "Standards Bigot," you'll ultimately embarass yourself since there are always counterexamples to any syntax-based rule. If on the other hand you emphasize broad goals and guidelines (e.g., "major on the majors," or "put the most important thing first on the line," or even "make sure your code is obvious and readable"), you're usually much better off. Code must be written to be read, not by the compiler, but by another human being. ============================================================================== [26.6] Should I declare locals in the middle of a function or at the top? Declare near first use. 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 function, you should create it half way down the function when it can be initialized correctly. Don't initialize it to an "empty" value at the top then "assign" it later. The reason for this is runtime performance. Building an object correctly is faster than building it incorrectly and remodeling it later. 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 350%, but there will be degradation. Unnecessary degradation. A common retort to the above is: "we'll provide set() member functions 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 a set() member function 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 member objects, but the fact that you're using a List and a String and a float, for example, is open for all to see. Bottom line: Locals should be declared near their first use. Sorry that this isn't familiar to C experts, but new doesn't necessarily mean bad. ============================================================================== [26.7] What source-file-name convention is best? foo.cpp? foo.C? foo.cc? If you already have a convention, use it. If not, consult your compiler to see what the compiler expects. Typical answers are: .cpp, .C, .cc, or .cxx (naturally the .C extension assumes a case-sensitive file system to distinguish .C from .c). We've often used .cpp for our C++ source files, and we have also used .C. In the latter case, when porting to case-insensitive file systems you need to tell the compiler to treat .c files as if they were C++ source files (e.g., -Tdp for IBM CSet++, -cpp for Zortech C++, -P for Borland C++, etc.). The point is that none of these filename extensions are uniformly superior to the others. We generally use whichever technique is preferred by our customer (again, these issues are dominated by business considerations, not by technical considerations). ============================================================================== [26.8] What header-file-name convention is best? foo.H? foo.hh? foo.hpp? If you already have a convention, use it. If not, and if you don't need your editor to distinguish between C and C++ files, simply use .h. Otherwise use whatever the editor wants, such as .H, .hh, or .hpp. We've tended to use either .hpp or .h for our C++ header files. ============================================================================== [26.9] Are there any lint-like guidelines for C++? 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 Fred's assignment operator should return *this as a Fred& (allows chaining of assignments) * A class with any virtual[20] functions ought to have a virtual destructor[20.5] * A class with any of {destructor, assignment operator, copy constructor} generally needs all 3 * A class Fred's copy constructor and assignment operator should have const in the parameter: respectively Fred::Fred(const Fred&) and Fred& Fred::operator= (const Fred&) * When initializing an object's member objects in the constructor, always use initialization lists rather than assignment. The performance difference for user-defined classes can be substantial (3x!) * Assignment operators should make sure that self assignment[12.1] does nothing, otherwise you may have a disaster[12.2]. In some cases, this may require you to add an explicit test to your assignment operators[12.3]. * In classes that define both += and +, a += b and a = a + b should generally do the same thing; ditto for the other identities of built-in/intrinsic types (e.g., a += 1 and ++a; p[i] and *(p+i); etc). This can be enforced by writing the binary operations using the op= forms. E.g., Fred operator+ (const Fred& a, const Fred& b) { Fred ans = a; ans += b; return ans; } This way the "constructive" binary operators don't even need to be friends[14]. But it is sometimes possible to more efficiently implement common operations (e.g., if class Fred is actually std::string, and += has to reallocate/copy string memory, it may be better to know the eventual length from the beginning). ============================================================================== [26.10] Why do people worry so much about pointer casts and/or reference casts? Because they're evil[6.14]! (Use them sparingly and with great care.) For some reason, programmers are sloppy in their use of pointer casts. They cast this to that all over the place, then they wonder why things don't quite work right. Here's the worst thing: when the compiler gives them an error message, they add a cast to "shut the compiler up," then they "test it" to see if it seems to work. If you have a lot of pointer casts or reference casts, read on. The compiler will often be silent when you're doing pointer-casts and/or reference casts. Pointer-casts (and reference-casts) tend to shut the compiler up. I think of them as a filter on error messages: the compiler wants to complain because it sees you're doing something stupid, but it also sees that it's not allowed to complain due to your pointer-cast, so it drops the error message into the bit-bucket. It's like putting duct tape on the compiler's mouth: it's trying to tell you something important, but you've intentionally shut it up. A pointer-cast says to the compiler, "Stop thinking and start generating code; I'm smart, you're dumb; I'm big, you're little; I know what I'm doing so just pretend this is assembly language and generate the code." The compiler pretty much blindly generates code when you start casting -- you are taking control (and responsibility!) for the outcome. The compiler and the language reduce (and in some cases eliminate!) the guarantees you get as to what will happen. You're on your own. By way of analogy, even if it's legal to juggle chainsaws, it's stupid. If something goes wrong, don't bother complaining to the chainsaw manufacturer -- you did something they didn't guarantee would work. You're on your own. (To be completely fair, the language does give you some guarantees when you cast, at least in a limited subset of casts. For example, it's guaranteed to work as you'd expect if the cast happens to be from an object-pointer (a pointer to a piece of data, as opposed to a pointer-to-function or pointer-to-member) to type void* and back to the same type of object-pointer. But in a lot of cases you're on your own.) ============================================================================== [26.11] Which is better: identifier names that_look_like_this or identifier names thatLookLikeThis? It's a precedent thing. If you have a Pascal or Smalltalk background, youProbablySquashNamesTogether like this. If you have an Ada background, You_Probably_Use_A_Large_Number_Of_Underscores like this. If you have a Microsoft Windows background, you probably prefer the "Hungarian" style which means you jkuidsPrefix vndskaIdentifiers ncqWith ksldjfTheir nmdsadType. And then there are the folks with a Unix C background, who abbr evthng n use vry srt idntfr nms. (AND THE FORTRN PRGMRS LIMIT EVRYTH TO SIX LETTRS.) So there is no universal standard. If your project team has a particular coding standard for identifier names, use it. But starting another Jihad over this will create a lot more heat than light. From a business perspective, there are only two things that matter: The code should be generally readable, and everyone on the team should use the same style. Other than that, th difs r minr. One more thing: don't import a coding style onto platform-specific code where it is foreign. For example, a coding style that seems natural while using a Microsoft library might look bizarre and random while using a UNIX library. Don't do it. Allow different styles for different platforms. (Just in case someone out there isn't reading carefully, don't send me email about the case of common code that is designed to be used/ported to several platforms, since that code wouldn't be platform-specific, so the above "allow different styles" guideline doesn't even apply.) Okay, one more. Really. Don't fight the coding styles used by automatically generated code (e.g., by tools that generate code). Some people treat coding standards with religious zeal, and they try to get tools to generate code in their local style. Forget it: if a tool generates code in a different style, don't worry about it. Remember money and time?!? This whole coding standard thing was supposed to save money and time; don't turn it into a "money pit." ============================================================================== [26.12] Are there any other sources of coding standards? Yep, there are several. Here are a few sources that you might be able to use as starting points for developing your organization's coding standards (in random order): * cdfsga.fnal.gov/computing/coding_guidelines/CodingGuidelines.html * www.nfra.nl/~seg/cppStdDoc.html * www.cs.umd.edu/users/cml/resources/cstyle * www.cs.rice.edu/~dwallach/CPlusPlusStyle.html * cpptips.hyperformix.com/conventions/cppconventions_1.html * www.objectmentor.com/resources/articles/naming.htm * www.arcticlabs.com/codingstandards/ * www.possibility.com/cpp/CppCodingStandard.html * www.cs.umd.edu/users/cml/cstyle/Wildfire-C++Style.html * The Ellemtel coding guidelines are available at - www.cs.umd.edu/users/cml/cstyle/Ellemtel-rules.html - www.doc.ic.ac.uk/lab/cplus/c++.rules/ - www.mgl.co.uk/people/kirit/cpprules.html Note: I do NOT warrant or endorse these URLs and/or their contents. They are listed as a public service only. I haven't checked their details, so I don't know if they'll help you or hurt you. Caveat emptor. ============================================================================== [26.13] Should I use "unusual" syntax? Only when there is a compelling reason to do so. In other words, only when there is no "normal" syntax that will produce the same end-result. Software decisions should be made based on money. Unless you're in an ivory tower somewhere, when you do something that increases costs, increases risks, increases time, or, in a constrained environment, increases the product's space/speed costs, you've done something "bad." In your mind you should translate all these things into money. Because of this pragmatic, money-oriented view of software, programmers should avoid non-mainstream syntax whenever there exists a "normal" syntax that would be equivalent. If a programmer does something obscure, other programmers are confused; that costs money. These other programmers will probably introduce bugs (costs money), take longer to maintain the thing (money), have a harder time changing it (missing market windows = money), have a harder time optimizing it (in a constrained environment, somebody will have to spend money for more memory, a faster CPU, and/or a bigger battery), and perhaps have angry customers (money). It's a risk-reward thing: using abnormal syntax carries a risk, but when an equivalent, "normal" syntax would do the same thing, there is no "reward" to ameliorate that risk. For example, the techniques used in the Obfuscated C Code Contest <http://www.ioccc.org/> are, to be polite, non-normal. Yes many of them are legal, but not everything that is legal is moral. Using strange techniques will confuse other programmers. Some programmers love to "show off" how far they can push the envelope, but that puts their ego above money, and that's unprofessional. Frankly anybody who does that ought to be fired. (And if you think I'm being "mean" or "cruel," I suggest you get an attitude adjustment. Remember this: your company hired you to help it, not to hurt it, and anybody who puts their own personal ego-trips above their company's best interest simply ought to be shown the door.) As an example of non-mainstream syntax, it's not "normal" to use the ?: operator as a statement. (Some people don't even like it as an expression, but everyone must admit that there are a lot of uses of ?: out there, so it is "normal" (as an expression) whether people like it or not[26.5].) Here is an example of using using ?: as a statement: blah(); blah(); xyz() ? foo() : bar(); // should replace with if/else blah(); blah(); Same goes with using || and && as if they are "if-not" and "if" statements, respectively. Yes, those are idioms in Perl, but C++ is not Perl and using these as replacements for if statements (as opposed to using them as expressions) is just not "normal" in C++. Example: foo() || bar(); // should replace with if (!foo()) bar(); foo() && bar(); // should replace with if (foo()) bar(); Here's another example that seems to work and may even be legal, but it's certainly not normal: void f(const& MyClass x) // use const MyClass& x instead { ... } ============================================================================== SECTION [27]: Learning OO/C++ [27.1] What is mentoring? It's the most important tool in learning OO. Object-oriented thinking is caught, not just taught. Get cozy with someone who really knows what they're talking about, and try to get inside their head and watch them solve problems. Listen. Learn by emulating. If you're working for a company, get them to bring someone in who can act as a mentor and guide. We've seen gobs and gobs of money wasted by companies who "saved money" by simply buying their employees a book ("Here's a book; read it over the weekend; on Monday you'll be an OO developer"). ============================================================================== [27.2] Should I learn C before I learn OO/C++? Don't bother. If your ultimate goal is to learn OO/C++ and you don't already know C, reading books or taking courses in C will not only waste your time, but it will teach you a bunch of things that you'll explicitly have to un-learn when you finally get back on track and learn OO/C++ (e.g., malloc()[16.3], printf()[15.1], unnecessary use of switch statements[20], error-code exception handling[17], unnecessary use of #define macros[9.3], etc.). If you want to learn OO/C++, learn OO/C++. Taking time out to learn C will waste your time and confuse you. ============================================================================== [27.3] Should I learn Smalltalk before I learn OO/C++? Don't bother. If your ultimate goal is to learn OO/C++ and you don't already know Smalltalk, reading books or taking courses in Smalltalk will not only waste your time, but it will teach you a bunch of things that you'll explicitly have to un-learn when you finally get back on track and learn OO/C++ (e.g., dynamic typing[29.3], non-subtyping inheritance[29.5], error-code exception handling[17], etc.). Knowing a "pure" OO language doesn't make the transition to OO/C++ any easier. This is not a theory; we have trained and mentored literally thousands of software professionals in OO. In fact, Smalltalk experience can make it harder for some people: they need to unlearn some rather deep notions about typing and inheritance in addition to needing to learn new syntax and idioms. This unlearning process is especially painful and slow for those who cling to Smalltalk with religious zeal ("C++ is not like Smalltalk, therefore C++ is evil"). If you want to learn OO/C++, learn OO/C++. Taking time out to learn Smalltalk will waste your time and confuse you. Note: I sit on both the ANSI C++ (X3J16) and ANSI Smalltalk (X3J20) standardization committees[6.11]. I am not a language bigot[6.4]. I'm not saying C++ is better or worse than Smalltalk; I'm simply saying that they are different[29.1]. ============================================================================== [27.4] Should I buy one book, or several? At least three. There are three categories of insight and knowledge in OO programming using C++. You should get a great book from each category, not an okay book that tries to do an okay job at everything. The three OO/C++ programming categories are: * C++ legality guides -- what you can and can't do in C++[27.6]. * C++ morality guides -- what you should and shouldn't do in C++[27.5]. * Programming-by-example guides -- show lots of examples, normally making liberal use of the C++ standard library[27.7]. Legality guides describe all language features with roughly the same level of emphasis; morality guides focus on those language features that you will use most often in typical programming tasks. Legality guides tell you how to get a given feature past the compiler; morality guides tell you whether or not to use that feature in the first place. Meta comments: * Don't trade off these categories against each other. You shouldn't argue in favor of one category over the other. They dove-tail. * The "legality" and "morality" categories are both required. You must have a good grasp of both what can be done and what should be done. There is a fourth category you should consider in addition to the above three: OO Design books[27.8]. These are books that focus on how to think and design with objects. ============================================================================== [27.5] What are some best-of-breed C++ morality guides? Here's my personal (subjective and selective) short-list of must-read C++ morality guides, alphabetically by author: * Cline, Lomow, and Girou, C++ FAQs, Second Edition, 587 pgs, Addison-Wesley, 1999, ISBN 0-201-30983-1. Covers around 500 topics in a FAQ-like Q&A format. * Meyers, Effective C++, Second Edition, 224 pgs, Addison-Wesley, 1998, ISBN 0-201-92488-9. Covers 50 topics in a short essay format. * Meyers, More Effective C++, 336 pgs, Addison-Wesley, 1996, ISBN 0-201-63371-X. Covers 35 topics in a short essay format. Similarities: All three books are extensively illustrated with code examples. All three are excellent, insightful, useful, gold plated books. All three have excellent sales records. Differences: Cline/Lomow/Girou's examples are complete, working programs rather than code fragments or standalone classes. Meyers contains numerous line-drawings that illustrate the points. ============================================================================== [27.6] What are some best-of-breed C++ legality guides? Here's my personal (subjective and selective) short-list of must-read C++ legality guides, alphabetically by author: * Lippman and Lajoie, C++ Primer, Third Edition, 1237 pgs, Addison-Wesley, 1998, ISBN 0-201-82470-1. Very readable/approachable. * Stroustrup, The C++ Programming Language, Third Edition, 911 pgs, Addison-Wesley, 1998, ISBN 0-201-88954-4. Covers a lot of ground. Similarities: Both books are excellent overviews of almost every language feature. I reviewed them for back-to-back issues of C++ Report, and I said that they are both top notch, gold plated, excellent books. Both have excellent sales records. Differences: If you don't know C, Lippman's book is better for you. If you know C and you want to cover a lot of ground quickly, Stroustrup's book is better for you. ============================================================================== [27.7] What are some best-of-breed C++ programming-by-example guides? Here's my personal (subjective and selective) short-list of must-read C++ programming-by-example guides: * Koenig and Moo, Accelerated C++, 336 pgs, Addison-Wesley, 2000, ISBN 0-201-70353-X. Lots of examples using the standard C++ library. Truly a programming-by-example book. * Musser, STL Tutorial and Reference Guide, Addison-Wesley, 2001. Lots of examples showing how to use the STL portion of the standard C++ library, plus lots of nitty gritty detail. ============================================================================== [27.8] Are there other OO books that are relevant to OO/C++? Yes! Tons! The morality[27.5], legality[27.6], and by-example[27.7] categories listed above were for OO programming. The areas of OO analysis and OO design are also relevant, and have their own best-of-breed books. There are tons and tons of good books in these other areas. The seminal book on OO design patterns is (in my personal, subjective and selective, opinion) a must-read book: Gamma et al., Design Patterns, 395 pgs, Addison-Wesley, 1995, ISBN 0-201-63361-2. Describes "patterns" that commonly show up in good OO designs. You must read this book if you intend to do OO design work. ==============================================================================