Where Should You Apply Various C++ Coding Practices?
Dr. Love asks: "C++ contains a mountain of features. I am searching for a good C++ coding practice - what language features to avoid, what language features to make extensive use of, etc. In my survey of C++ coding practices, there seems to be a plethora of philosophies ranging from the stingy to the highly abstracted and elaborate. By stingy, I'm referring to those that use few of C++'s advanced features, such as Mozilla.
At the opposite end of the spectrum, there are elaborate practices
that make extensive use of the STL, exceptions, RTTI, templates,
etc. Those who prefer the simple implementations tend to argue that
elaborate practices result in inefficient, unclear, and unportable
code. Those who prefer the elaborate implementations tend to argue
that simple implementations are missing out on a Nirvana of reuse and
clean design. Are these arguments unfounded? What has been your
experience with C++ coding guidelines? What does the industry expect?" Just like the choice of the language itself, the choice of the features you use in your language should lend itself to the task you are developing. Some projects don't need to use every single feature of C++ to get the job done, however use of those ignored features in other projects might make your life (and your code) much easier to handle.
Hey, there are libraries that manage to avoid C++ constructs to abstract this sort of stuff. For example, B-tree operators could use a fixed structure in which there exists the data fields necessary for the library to do its thing, and the "payload" of the structure is a void pointer to the data referenced by the B-tree node. This means that you can have disk-resident B-trees by using the void pointer to refer instead to a byte offset; array-resident B-trees by using the void pointer to hold an index; value-oriented B-trees by having an integer value in the void pointer.
Oh, I know that you can duplicate most (or all) OO features in straight C. I've just run into the limits of practicality for this on a few occasions. At least twice I've caught myself implementing collections of function pointers in structures to emulate polymorphism. If I'm doing *that*, I might as well just use C++ and save myself a lot of hassles debugging typos.
YMMV. I realize that for some applications, having to trust automatic implementation is bad, but for most of what I work with it's worth it.
The feature that crops up in practice most for me is polymorphism.
I code mainly in C, not C++. I've just written yet another program that uses a binary tree to store several different types of data. The naive way of writing this is to make several different tree-node types and several sets of nearly-identical tree manipulation functions. The way I did it was to kludge things with a data type identifier and a union of the possible data stored.
The Right Way to implement this is to make an abstract class for the tree node, write the tree manipulation methods to work with the abstract class, and make derived classes that store different types of data, with appropriate constructors that initialize the data fields. Anything that doesn't have to care about the data type can just manipulate the objects as the original abstract class.
This situation has cropped up before. Any situation where I'm doing the same type of thing with many types of data can benefit from derived classes and polymorpism. This happens frequently with utility libraries that I write, but also crops up now and then with certain types of information storage task.
I still code in C most of the time, because I'm too lazy to dust off my C++ books and these are just my own projects. However, this is indeed a common set of cases where features of C++ would be useful.
NuMega's BoundsChecker is a program that can discover memory leaks in your program. I think it's only for NT.
Je ne parle pas francais.
I think there are always exceptions to any generalizations.
For instance, operator overloading. The std use it to make std::vector behave and look like an array. So you have something like
std::vector<int> v; int i = v[4];
Looks natural to me.
Nice thing about the stream I/O is that you don't have to deal with the damn %s%d%S crap. Sorry, printf is just evil compared to cout.
References, mostly agree, but you can save some time and memory by using reference in connection with const for objects. For instance,
int foo( const Obj& o );
Instead of making a copy of Obj, you know get a reference to the original object. Saves you time.
Je ne parle pas francais.
It sounds like you're looking for either Smalltalk or Objective-C. Check them out.
Mod down posts with a "Free Mac Mini/iPod" sig, they're spam!
Your comments about destructors and const goes to your last point. You're right, if you try to program Java as you do C++ you're hosed. I realize that Java and C++ are quite different (even ignoring memory management). My main point is that if I had to code in C++ again, I'd throw out the things that Java threw out.
I really think that Java got it right.
C++ is a "batteries not included" language: like C, but unlike Python or Java, its standard libraries are really quite small. For instance, despite the existence of the STL and C++'s powerful templates, there's not even an official, standard hashtable class (though most folks ship a vendor-specific or something of the sort). That said, library design was a MAJOR concern in the creation of C++, so it's possible to build some great stuff (look at Qt, which I think is quite nice). So, one of the big challenges in C++ is to find the right tools on which to base your project. Scott Meyers' "Effective C++" and "More Effective C++" would be pretty high on my list. If you have a great budget, check out Insure++ from www.parasoft.com. It catches MANY MANY common coding and runtime errors and is VERY COOL. Unlike simple bounds checkers, it even flags memory accesses that may be a problem at some point in the future, and it has tools to help you track memory usage over time, measure code coverage, etc. Failing that, you can use the memory checker built into C++ Builder 5 for free (well, free if you own the IDE) if you're on Windows. Also, look at some foundation libraries, if you're serious about building a large system and you don't want to reinvent the wheel. ACE (www.riverace.com) is free/open source and VERY, VERY extensive, but this comes with something of a learning curve. Common C++ (cplusplus.sourceforge.net) looked a bit friendlier last time I checked it out, but it was a little buggy (about a year ago, at least). On the commercial side, Qt is fantastic, and I've heard some decent reviews of Rogue Wave's Tools.h++. A well-designed foundation library will already have worked out a lot of the cross platform/cross compiler issues for you, and that alone can be worth the price of admission. Most of all: good luck! --JRZ
The good parts:
The bad parts:
"Look! I learned a new c++ feature today, there has to be some way I can jam this in somewhere in my project."
Be careful! Don't do it just because it's possible, but for a reason. Focus on the project's task and not the language it is written in.
Your code might end up like that abc rhyme "the lazy brown fox...", if you think your program will be more complete if every c++ language feature is used.
C++ is, like Perl, such a mess because the problem set is such a mess. That means it's pretty easy to shoot yourself in the foot--and given C++'s power, shots to the foot often take off everything below your waist.
If crossplatform, portable code is a necessity, then be very careful when using newer features such as templates. I've had the devil's own time taking code that works perfectly fine in GCC-2.96 (the Red Hat snapshot, which is a very fine C++ compiler), but breaks horribly under GCC-2.91. Code that compiles cleanly under MSVC++ will oftentimes break horribly in GCC-2.96, etc.
The fewer C++ features you use, the less chance you'll run into these gotchas. That's why the Mozilla team uses a restricted set of C++ functions, I think, far more than to enhance readability or whatnot--the simple fact is, the more C++ features you use, the harder it is to get it to compile under Foo compiler, because Foo doesn't properly implement such-and-such chapters of the Standard; and code written for the Foo compiler breaks on the Bar compiler, because Bar demands strict adherence to the spec, which Foo doesn't provide.
That being said: give consideration to at least using the inheritance features of C++'s class system. Inheritance is, if anything, the most useful feature of C++, and is well-supported by every compiler. Don't treat C++ like Ada83, where classes existed only to protect data as private and to create interfaces to other bits of functionality; let your C++ classes be hierarchial, let them inherit from base classes, and use that to your advantage.
After that, I don't have any recommendations. I'm personally a big fan of generic programming (templatized programming) and the STL. The STL is fearsomely efficient and fast. It's got a learning curve like the Matterhorn, but once you climb it, it's wonderful.
BTW, if anyone tells you to ignore templates on grounds that they cause bloat, please check out the home page of Amit Patel, a Doctoral student at Stanford (who, I believe, often reads Slashdot--if so, hi, Amit, drop me a line sometime) and friend of mine. According to benchmarks he provides on his page (click here), templatized code that makes use of the STL is often, if not almost always, of comparable executable size to an equivalent C program, and substantially faster.
Some compilers, such as Sun's, are absolutely awful with templates--that's probably how the nasty myth of templatized code being bloated and slow came to be. As a rule, though, there's no general truth to this myth; some compilers produce bloated code when using foo, bar and baz parts of the language, but it's not a characteristic of the language--just a characteristic of a crappy compiler.
Exceptions are a definite win. The syntax makes good sense and the semantics do pretty much what you would want. (Well, what I would want, anyway.) try, catch, and throw just about eliminate the need for goto or longjmp, and are much prettier besides. They're easy to use right, and it would take a deliberate act to use them wrong.
Inheritance and polymorphism are good when used judiciously, but can definitely hurt you; spaghetti inheritance is one of the ugliest things around. Each class should abstract some thing or concept in the system; if that's not the case, it's time to start chopping things out of your inheritance hierarchy.
The STL (and C++ template in general) syntax gives me a headache, personally; good idea, but seems glommed onto the language. Other than the occasional container class, which is simple enough, I avoid it.
Good question for discussion, by the way!
Tom Swiss | the infamous tms | http://www.infamous.net/
Tom Swiss | the infamous tms | my blog
You cannot wash away blood with blood
If you want someone to tell you if your code sucks, call the expert. Bjarne Stroustrup's "The C++ Programming Language" should be required reading. He talks about a lot of this stuff, and tells when each C++ feature should and should not be used. Highly recommended.
Time flies like an arrow. Fruit flies like a banana.
My java lecturer tried to convince me that inheritence was what was to be used in one case where it obviously wasn't. Late in the semester the whole lecture theatre had a good laugh when she wasnt able to change directory in dos to execute javadoc. Pity by that time I had chosen to spend the lecture time somewhere slighty more useful in front of the sun api docs.
Geez I hate it when people with a science degree with a major in computing try to teach IT, it really doesnt work, at least in my experience.
I think instead of trying to use features of a language one should try and recognize design patterns and apply them to your project. Pick up the book "Design Patterns: Elements of Reusable Object Oriented Software" by the GoF. It will be time well spent.
I've heard tell that it takes about a year with the language to get to be a really good C++ programmer. I wonder if that assumes that you have someone who can tell you why your code sucks. If you fall into the bad habits of the newbie programmer and they don't get corrected, I can't see a programmer ever getting better at it.
I'm trying to teach myself to set people on fire with my mind... Is it hot in here?
Next is the STL. The STL is a fairly recent newcomer to C++ as are templates themselves. The STL can save you a lot of time that you'd spend reimplementing the same data structures. It's an excellent resource. There are other cool things you can do with templates as well -- the GTK-- signal system is way cool, for instance.
Finally, check out here (http://hjem.get2net.dk/nimrod/tipdesign.htm for the goat wary.) It's a bunch of archived articles from the C++ netnews group. There are plenty of good coding practise pointers and code snippets and it's a great resource.
These three resources should get you well ahead of the curve when it comes to making good use of the C++ features.
I'm trying to teach myself to set people on fire with my mind... Is it hot in here?
Bruce Eckel's books are also pretty good (Thinking in C++ (2nd), also Thinking in (several other languages/ patterns)).
--
News for geeks in Austin: www.geekaustin.org
News for Geeks in Austin, TX
I Agree, use exceptions. Here is a good text on it: http://www.relisoft.com/book/tech/5resource.html
Works in 9x as well
If Microsoft's STL is a pain in the ass, use:
l
http://www.stlport.org/product.html
Its free, VERY robust, portable and exception safe.
Note that in Microsoft's compilers you need to make sure that the "new" operator throws a std::bad_alloc exception instead of returning NULL when memory exhaustion occurs...here is how to:
http://www.relisoft.com/book/tech/5resource.htm
My experiences with a 2,000,000 lines of code C++ server project (in winNT):
r ce.html).
.
1) ALLWAYS use SmartPointers unless you have a compelling reason to not use them.
2) Memory leaks are going to be your biggest problem, see rule number 1.
3) Use ASSERT like crazy...everywhere, if you are expecting some valid imput in a method, make sure it's explicilty stated with an ASSERT. ASSERT is the best way to catch an error on the spot.
4) Make the compiler generate debug symbols in release as a separate file, you WILL find bugs that only happen in RELEASE versions.
5) Use boundschecker to debug memory problems.
7) Don't use catch(...) on any other place than your main function...if you need to, rethrow the exception and let the debugger catch it. Never catch an exception that you don't know how to handle on the current scope.
6) Make your code exception safe, it's the only way to deal with memory exhaustion problems(http://www.relisoft.com/book/tech/5resou
7) If you are using multiple threads and a large number of locks, use smartlocks, otherwise you will have deadlocks (it's painful but does pay off).
8) If you application is multithreaded, use a static locking order, otherwise you will have deadlocks.
9) NEVER, NEVER, NEVER create your own collections or strings, use STL.
10) If you use Visual C++ and STL, make sure you replace the source files with the fixed versions(http://www.dinkumware.com/vc_fixes.html)
11) Read "Effective C++" and "More effective C++" (look them up in amazon)
12) Avoid Runtime Type Identification, most of the times you don't need it.
send me a message at andresmurillo@go.com if you need more info.
On the other hand, forbidding a feature (like templates) just because it's complicated can be stupid and self-defeating. Are you really writing your own Map class? Are you sure it's right? What do you do when you need a Map from Foos to Bars, and the only one you have is from Bazes to Quuxes? You don't copy and paste, do you? And your Map doesn't subvert the type system, right? And if you decide you should be using a Set instead of a List, you can fix this with one line of code, right?
Your favorite
I met Bruce Eckel at SD98. He was wearing a ringmaster's suit and standing behind a folding table with a guy in a gorilla suit. He was hawking some service, but I can't remember what it was. I did have a good laugh, though.
I laughed there almost as hard as I did when I saw the Sun Javabean having to be escorted by security guards there to prevent people from tipping it over. Now that was funny.
Dancin Santa
You can find book 1 here.
And book 2 in the series here.
And if you're so inclined, the CD can be found here.
You may be boycotting that site, so check out the following links.
Book 1
Book 2
The CD
Dancin Santa
At work, the biggest feature of C++ that we insist that everyone use are exceptions. Using exceptions helps us keep code clean and readable and gets rid of multiple nested if statements everytime we have to test for an error condition. The ability to throw an exception and simply catch them all at the bottom makes debugging a lot simpler.
Other features that we use include STL, for the simplicity of creating lists and other data structures, and polymorphism. Since our entire project is an object oriented design, we can't get away from it, and in many cases, it greatly reduces the amount of code we have to write and debug.
That said, however, It is important that you design your object heirarchy and relationships before hand and test to insure that it really will perform the task you intend it to. Otherwise, it turns into an ugly mess.
-> Capt Cosmic <-