C++ Coding StandardLast Modified: 2008-03-01tmh@possibility.com / http://www.possibility.com/Tmh
Contents
IntroductionStandardization is ImportantIt helps if the standard annoys everyone in some way so everyone feels they are on the same playing field. The proposal here has evolved over many projects, many companies, and literally a total of many weeks spent arguing. It is no particular person's style and is certainly open to local amendments.Good PointsWhen a project tries to adhere to common standards a few good things happen:
Bad PointsNow the bad:
DiscussionThe experience of many projects leads to the conclusion that using coding standards makes the project go smoother. Are standards necessary for success? Of course not. But they help, and we need all the help we can get! Be honest, most arguments against a particular standard come from the ego. Few decisions in a reasonable standard really can be said to be technically deficient, just matters of taste. So be flexible, control the ego a bit, and remember any project is fundamentally a team effort.
Standards EnforcementFirst, any serious concerns about the standard should be brought up and worked out within the group. Maybe the standard is not quite appropriate for your situation. It may have overlooked important issues or maybe someone in power vehemently disagrees with certain issues :-)In any case, once finalized hopefully people will play the adult and understand that this standard is reasonable, and has been found reasonable by many other programmers, and therefore is worthy of being followed even with personal reservations. Failing willing cooperation it can be made a requirement that this standard must be followed to pass a code inspection. Failing that the only solution is a massive tickling party on the offending party. Accepting an Idea
6 Phases of a Project
Flow Chart for Project Decision Making
+---------+
| START |
+---------+
|
V
YES +------------+ NO
+---------------| DOES THE |---------------+
| | DAMN THING | |
V | WORK? | V
+------------+ +------------+ +--------------+ NO
| DON'T FUCK | | DID YOU FUCK |-----+
| WITH IT | | WITH IT? | |
+------------+ +--------------+ |
| | |
| | YES |
| V |
| +------+ +-------------+ +---------------+ |
| | HIDE | NO | DOES ANYONE |<------| YOU DUMBSHIT! | |
| | IT |<----| KNOW? | +---------------+ |
| +------+ +-------------+ |
| | | |
| | V |
| | +-------------+ +-------------+ |
| | | YOU POOR | YES | WILL YOU | |
| | | BASTARD |<------| CATCH HELL? |<-----+
| | +-------------+ +-------------+
| | | |
| | | | NO
| | V V
| V +-------------+ +------------+
+-------------->| STOP |<------| SHITCAN IT |
+-------------+ +------------+
LeadershipI wish i had said this, but it was said by asd@asd.com in comp.software-eng.Leaders:
Programmer #1 says " x = 1" That's when a Project Leader is required. Unless you want to flip a coin. Oh yah - one more thing. Project leaders: TAKE the blame when things go wrong and SHARE the credit when things go right. Ain't easy - but it's the way I try to run my life.
Resources- Take a Look!General
Book RecommendationsWhat are some good C++ books you can buy for you and your team?
NamesMake Names FitNames are the heart of programming. In the past people believed knowing someone's true name gave them magical power over that person. If you can think up the true name for something, you give yourself and the people coming after power over the code. Don't laugh!A name is the result of a long deep thought process about the ecology it lives in. Only a programmer who understands the system as a whole can create a name that "fits" with the system. If the name is appropriate everything fits together naturally, relationships are clear, meaning is derivable, and reasoning from common human expectations works as expected. If you find all your names could be Thing and DoIt then you should probably revisit your design. Class Names
Method and Function Names
Include Units in NamesIf a variable represents time, weight, or some other unit then include the unit in the name so developers can more easily spot problems. For example:uint32 mTimeoutMsecs; uint32 mMyWeightLbs;Better yet is to make a variable into a class so bad conversions can be caught. No All Upper Case Abbreviations
Justification
Exampleclass FluidOz // NOT FluidOZ class NetworkAbcKey // NOT NetworkABCKey Class Names
Justification
Exampleclass NameOneTwo class Name Class Library Names
ExampleJohn Johnson's complete data structure library could use JJ as a prefix, so classes would be:
class JjLinkList
{
}
Method Names
Justification
Example
class NameOneTwo
{
public:
int DoIt();
void HandleError();
}
Class Attribute Names
Justification
Example
class NameOneTwo
{
public:
int VarAbc();
int ErrorNumber();
private:
int mVarAbc;
int mErrorNumber;
String* mpName;
}
Method Argument Names
Justification
Example
class NameOneTwo
{
public:
int StartYourEngines(
Engine& rSomeEngine,
Engine& rAnotherEngine);
}
Variable Names on the Stack
Justification
Example
int
NameOneTwo::HandleError(int errorNumber)
{
int error= OsErr();
Time time_of_error;
ErrorProcessor error_processor;
Time* p_out_of_time= 0;
}
The standard pointer notation is not entirely satisfactory because it doesn't look quite right, but it is consistent. How do you handle statics? There's never a reason to have a static local to a function so there's no reason to invent a syntax for it. But like for most absolute rules, there is an exception, that is when making singletons. Use a "s_" prefix in this case. Take a look at Singleton Pattern for more details.
Pointer Variables
Justification
ExampleString* pName= new String; String* pName, name, address; // note, only pName is a pointer. Reference Variables and Functions Returning References
Justification
Example
class Test
{
public:
void DoSomething(StatusInfo& rStatus);
StatusInfo& rStatus();
const StatusInfo& Status() const;
private:
StatusInfo& mrStatus;
}
Global Variables
Justification
Example
Logger gLog;
Logger* gpLog;
Global Constants
JustificationIt's tradition for global constants to named this way. You must be careful to not conflict with other global #defines and enum labels.Example
const int A_GLOBAL_CONSTANT= 5;
Static Variables
Justification
Example
class Test
{
public:
private:
static StatusInfo msStatus;
}
Type Names
Justification
Exampletypedef uint16 ModuleType; typedef uint32 SystemType; Enum NamesLabels All Upper Case with '_' Word SeparatorsThis is the standard rule for enum labels.Example
enum PinStateType
{
PIN_OFF,
PIN_ON
};
Enums as Constants without Class ScopingSometimes people use enums as constants. When an enum is not embedded in a class make sure you use some sort of differentiating name before the label so as to prevent name clashes.Example
enum PinStateType If PIN was not prepended a conflict
{ would occur as OFF and ON are probably
PIN_OFF, already defined.
PIN_ON
};
Enums with Class ScopingJust name the enum items what you wish and always qualify with the class name: Aclass::PIN_OFF.Make a Label for an Error StateIt's often useful to be able to say an enum is not in any of its valid states. Make a label for an uninitialized or error state. Make it the first label if possible.Example
enum { STATE_ERR, STATE_OPEN, STATE_RUNNING, STATE_DYING};
#define and Macro Names
JustificationThis makes it very clear that the value is not alterable and in the case of macros, makes it clear that you are using a construct that requires care.Some subtle errors can occur when macro names and enum labels use the same name. Example#define MAX(a,b) blah #define IS_ERR(err) blah C Function Names
Justification
Example
int
some_bloody_function()
{
}
C++ File ExtensionsIn short: Use the .h extension for header files and .cc for source files.For some reason an odd split occurred in early C++ compilers around what C++ source files should be called. C header files always use the .h and C source files always use the .c extension. What should we use for C++? The short answer is as long as everyone on your project agrees it doesn't really matter. The build environment should be able to invoke the right compiler for any extension. Historically speaking here have been the options:
Header File Extension DiscussionUsing .hh extension is not widely popular but makes a certain kind of sense. C header files use .h file extension and C++ based header files use .hh file extension. The problem is if we consider a header file an interface to a service then we can have a C interface to a service and C++ interface to the service in the same file. Using preprocessor directives this is possible and common. The recommendation is to stick with using the .h extension.
Source File Extension DiscussionThe problem with the .C extension is that it is indistinguishable from the .c extensions in operating systems that aren't case sensitive. Yes, this is a UNIX vs. windows issue. Since it is a simple step aiding portability we won't use the .C extension. The .cpp extension is a little wordy. So the .cc extension wins by default.
DocumentationComments Should Tell a StoryConsider your comments a story describing the system. Expect your comments to be extracted by a robot and formed into a man page. Class comments are one part of the story, method signature comments are another part of the story, method arguments another part, and method implementation yet another part. All these parts should weave together and inform someone else at another point of time just exactly what you did and why.Document DecisionsComments should document decisions. At every point where you had a choice of what to do place a comment describing which choice you made and why. Archeologists will find this the most useful information.Use Extractable HeadersUse a document extraction system like Doxygen when documenting your code.These headers are structured in such a way as they can be parsed and extracted. They are not useless like normal headers. So take time to fill them out. If you do it right once no more documentation may be necessary. As part of your nighlty build system have a step the generates the documentation from the source. Then index the source using a tool like Lucene. Have a front end to the search so developers can do full text searches on nightly builds and for release builds. This is a wonderfully useful feature. The next step in automation is to front the repository with a web server documentation can directly refer to a source file with a URL.
Comment All Questions a Programmer May Have When Looking at Your CodeAt every point in your code think about what questions a programmer may have about the code. It's crucial you answer all those questions somehow, someway. If you don't, as the code writer, answer those questions, who will?If you think your code is so clear and wonderful that nobody will have any questions then you are lying to yourself. I have never seen a large system with this wonderful self-documenting code feature. I've seen very few small libraries are even a single class that are so wonderfully self-documented. You have a lot of tools at your disposal to answer questions:
I don't really consider unit tests a question answering device because if you can't understand the code by reading it, reading something else about the code you don't understand won't help you understand it better.
Make Your Code Discoverable by BrowsingProgrammers should be able to navigate your code by looking at markers in the code, namely the names and the directory structure. Nothing is more frustrating to than to have to look at pile of code and have no idea what it's organizing principles are.Have a logicanl directory structure. Have directories called doc, lib, src, bin, test, pkg, install, etc and whatever, so I at least have some idea where stuff is. People use the weirdest names and lump everything together so that it it can be detangles. Clear thought is evidenced from the beginning by a directory stucture. Don't put more than one class in a file. Otherwise, how will I know its there when I browse your code? Should I really need to use search to find every last thing? Can't I just poke around the code and find it? I can if you organize your code. Name your files after your classes. I didn't believe this one until I saw it. Why you name a file different than the class? How I am possibly supposed to know what's in the file otherwise?
Write Comments as You CodeYou won't every go back later and document your code. You just won't. Don't lie to yourself, the world, and your mother by saying that you will.So when you do something document it right then and there. When you create a class- document it. When you create a method- document it. And so on. That way when you finish coding you will also be finished documenting. I advocate simultaneously writing code, writing tests, and writing documentaiton. Which comes first depends on you and the problem. I don't think there is any rule that says which should come first. On the path to getting stuff done I'll take the entrance that seems easiest to me at the time. Once on the path it's easy to follow the entire trail. Won't this break the flow? No, I think it improves flow because it keeps you mindful of what you are doing, why you are doing, and how it fits in the big picture. My take on TDD (test driven development) is that it's not the tests that are really important, it's that the tests keep you mindful while programming. A test means you are keeping everything in you mind at once you need to remember to successfully code something up. As you can't keep large chunks in your mind then smaller chunks are better. Writing a test forces you to remember what your code is supposed to accomplish. It's forcing you to also think about the use case/story/intent behind why you are writing the code. The result is a pointed mind that has focussed all its powers on doing one thing. When you can bring that focus to you programming you can be successful. The tests are really secondary. If your system/acceptance tests can't find bugs you are screwed anyway. And I find code written mindfully, one step at a time, has very few bugs. Unit tests are just one definition of a "step." You can use the orignial story you are implementing as a step as well. I use unit tests more as a mental focussing device while developing, like Zen Archery, than for the actual tests. After development unit tests are very useful in making sure code doesn't break. So I am not saying unit tests aren't useful. I just don't think they are the real reason behind why TDD generates working code. With a clear well functioning focussed mind we generate working code. But getting into that state is hard. Writing comments simultaneously with all other aspects of development deepens your mindfulness because you are thinking about everything at once. All the interconnections are present in your brain because you are explaining the intent behind what you are doing. There's a saying that you don't know something until you teach it. Comments are teaching what you are doing to someone else. When you are writing comments you must generate the thoughts to teach, to explain to someone else the intent behing what you are doing. It's very difficult to make a coding error when all this context is hot in your mind. I'll go back and forth between documenting, testing, and coding. I'll let the problem dictate what happens when as I am working my way through solving the problem. Saying testing should always come first is too simple a rule and I think misses the larger point about software development. Software is ultimately mind stuff. Using our minds better is the real methodology.
Make Gotchas ExplicitExplicitly comment variables changed out of the normal control flow or other code likely to break during maintenance. Embedded keywords are used to point out issues and potential problems. Consider a robot will parse your comments looking for keywords, stripping them out, and making a report so people can make a special effort where needed.
Gotcha Keywords
Gotcha Formatting
Example// :TODO: tmh 960810: possible performance problem // We should really use a hash table here but for now we'll // use a linear search. // :KLUDGE: tmh 960810: possible unsafe type cast // We need a cast here to recover the derived type. It should // probably use a virtual method or template. See AlsoSee Interface and Implementation Documentation for more details on how documentation should be laid out.Interface and Implementation DocumentationThere are two main audiences for documentation:
Class UsersClass users need class interface information which when structured correctly can be extracted directly from a header file. When filling out the header comment blocks for a class, only include information needed by programmers who use the class. Don't delve into algorithm implementation details unless the details are needed by a user of the class. Consider comments in a header file a man page in waiting.Class ImplementorsClass implementors require in-depth knowledge of how a class is implemented. This comment type is found in the source file(s) implementing a class. Don't worry about interface issues. Header comment blocks in a source file should cover algorithm issues and other design decisions. Comment blocks within a method's implementation should explain even more.Directory DocumentationEvery directory should have a README file that covers:
Include Statement DocumentationInclude statements should be documented, telling the user why a particular file was included. If the file includes a class used by the class then it's useful to specify a class relationship:
Example#ifndef XX_h #define XX_h // SYSTEM INCLUDES // #includeNotice how just by reading the include directives the code is starting to tell you a story of why and how it was built. Block CommentsUse comments on starting and ending a Block:
{
// Block1 (meaningful comment about Block1)
... some code
{
// Block2 (meaningful comment about Block2)
... some code
} // End Block2
} // End Block1
This may make block matching much easier to spot when you
don't have an intelligent editor.
Complexity ManagementLayeringLayering is the primary technique for reducing complexity in a system. A system should be divided into layers. Layers should communicate between adjacent layers using well defined interfaces. When a layer uses a non-adjacent layer then a layering violation has occurred.A layering violation simply means we have dependency between layers that is not controlled by a well defined interface. When one of the layers changes code could break. We don't want code to break so we want layers to work only with other adjacent layers. Sometimes we need to jump layers for performance reasons. This is fine, but we should know we are doing it and document appropriately.
Minimize Dependencies with Abstract Base ClassesOne of the most important strategies in C++ is to remove dependencies among different subsystems. Abstract base classes (ABCs) are a solid technique for dependency removal.An ABC is an abstraction of a common form such that it can be used to build more specific forms. An ABC is a common interface that is reusable across a broad range of similar classes. By specifying a common interface as long as a class conforming to that interface is used it doesn't really matter what is the type of the derived type. This breaks code dependencies. New classes, conforming to the interface, can be substituted in at will without breaking code. In C++ interfaces are specified by using base classes with virtual methods. The above is a bit rambling because it's a hard idea to convey. So let's use an example: We are doing a GUI where things jump around on the screen. One approach is to do something like:
class Frog
{
public:
void Jump();
}
class Bean
{
public:
void Jump();
}
The GUI folks could instantiate each object and call the Jump
method of each object. The Jump method of each object contains
the implementation of jumping behavior for that type of object.
Obviously frogs and beans jump differently even though both
can jump.
Unfortunately the owner of Bean didn't like the word Jump so they changed the method name to Leap. This broke the code in the GUI and one whole week was lost. Then someone wanted to see a horse jump so a Horse class was added:
class Horse
{
public:
void Jump();
}
The GUI people had to change their code again to add Horse.
Then someone updated Horse so that its Jump behavior was slightly different. Unfortunately this caused a total recompile of the GUI code and they were pissed. Someone got the bright idea of trying to remove all the above dependencies using abstract base classes. They made one base class that specified an interface for jumping things:
class Jumpable
{
public:
virtual void Jump() = 0;
}
Jumpable is a base class because other classes need to derive
from it so they can get Jumpable's interface. It's an
abstract base class because one or more of its methods has
the = 0 notation which means the method is a
pure virtual method. Pure virtual methods
must be implemented by derived classes. The compiler
checks.
Not all methods in an ABC must be pure virtual, some may have an implementation. This is especially true when creating a base class encapsulating a process common to a lot of objects. For example, devices that must be opened, diagnostics run, booted, executed, and then closed on a certain event may create an ABC called Device that has a method called LifeCycle which calls all other methods in turn thus running through all phases of a device's life. Each device phase would have a pure virtual method in the base class requiring implementation by more specific devices. This way the process of using a device is made common but the specifics of a device are hidden behind a common interface. Back to Jumpable. All the classes were changed to derive from Jumpable:
class Frog : public Jumpable
{
public:
virtual void Jump() { ... }
}
etc ...
We see an immediate benefit: we know all classes derived from
Jumpable must have a Jump method. No one can go changing
the name to Leap without the compiler complaining. One dependency
broken.
Another benefit is that we can pass Jumpable objects to the GUI, not specific objects like Horse or Frog:
class Gui
{
public:
void MakeJump(Jumpable*);
}
Gui gui;
Frog* pFrog= new Frog;
gui.MakeJump(pFrog);
Notice Gui doesn't even know it's making a frog jump, it
just has a jumpable thing, that's all it cares about. When
Gui calls the Jump method it will get the implementation
for Frog's Jump method. Another dependency down. Gui
doesn't have to know what kind of objects are jumping.
We also removed the recompile dependency. Because Gui doesn't contain any Frog objects it will not be recompiled when Frog changes. DownsideWow! Great stuff! Yes but there are a few downsides:Overhead for Virtual MethodsVirtual methods have a space and time penalty. It's not huge, but should be considered in design.Make Everything an ABC!Sometimes people overdo it, making everything an ABC. The rule is make an ABC when you need one not when you might need one. It takes effort to design a good ABC, throwing in a virtual method doesn't an ABC make. Pick and choose your spots. When some process or some interface can be reused and people will actually make use of the reuse then make an ABC and don't look back.Liskov's Substitution Principle (LSP)This principle states:All classes derived from a base class should be interchangeable when used as a base class.The idea is users of a class should be able to count on similar behavior from all classes that derive from a base class. No special code should be necessary to qualify an object before using it. If you think about it violating LSP is also violating the Open/Closed principle because the code would have to be modified every time a derived class was added. It's also related to dependency management using abstract base classes. For example, if the Jump method of a Frog object implementing the Jumpable interface actually makes a call and orders pizza we can say its implementation is not in the spirit of Jump and probably all other objects implementing Jump. Before calling a Jump method a programmer would now have to check for the Frog type so it wouldn't screw up the system. We don't want this in programs. We want to use base classes and feel comfortable we will get consistent behaviour. LSP is a very restrictive idea. It constrains implementors quite a bit. In general people support LSP and have LSP as a goal. Open/Closed PrincipleThe Open/Closed principle states a class must be open and closed where:
In practice the Open/Closed principle simply means making good use of our old friends abstraction and polymorphism. Abstraction to factor out common processes and ideas. Inheritance to create an interface that must be adhered to by derived classes. In C++ we are talking about using abstract base classes . A lot. Register/Dispatch IdiomAnother strategy for reducing dependencies in a system is the Register/Dispatch Idiom (RDI). RDI treats large grained occurrences in a system as events. Events are identified by some unique identifier. Objects in the system register with a dispatch system for events or classes of events it is interested in. Objects that are event sources send events into the dispatch system so the dispatch system can route events to consumers.RDI separates producers and consumers on a distributed scale. Event producers and consumers don't have to know about each other at all. Consumers can drop out of the event stream by deregistering for events. New consumers can register for events at anytime. Event producers can drop out with no ill effect to event consumers, the consumer just won't get any more events. It is a good idea for producers to have an "I'm going down event" so consumers can react intelligently. Logically the dispatch system is a central entity. The implementation however can be quite different. For a highly distributed system a truly centralized event dispatcher would be a performance bottleneck and a single point of failure. Think of event dispatchers as being a lot of different processes cast about on various machines for redundancy purposes. Event processors communicate amongst each other to distribute knowledge about event consumers and producers. Much like a routing protocol distributes routing information to its peers. RDI works equally well in the small, in processes and single workstations. Parts of the system can register as event consumers and event producers making for a very flexible system. Complex decisions in a system are expressed as event registrations and deregistrations. No further level of cooperation required. More expressive event filters can also be used. The above proposal filters events on some unique ID. Often you want events filtered on more complex criteria, much like a database query. For this to work the system has to understand all data formats. This is easy if you use a common format like attribute value pairs. Otherwise each filter needs code understanding packet formats. Compiling in filter code to each dispatcher is one approach. Creating a downloadable generic stack based filter language has been used with success on other projects, being both simple and efficient.
DelegationDelegation is the idea of a method using another object's method to do the real work. In some sense the top layer method is a front for the other method. Delegation is a form of dependency breaking. The top layer method never has to change while it's implementation can change at will.Delegation is an alternative to using inheritance for implementation purposes. One can use inheritance to define an interface and delegation to implement the interface. Some people feel delegation is a more robust form of OO than using implementation inheritance. Delegation encourages the formation of abstract class interfaces and HASA relationships. Both of which encourage reuse and dependency breaking. Example
class TestTaker
{
public:
void WriteDownAnswer() { mPaidTestTaker.WriteDownAnswer(); }
private:
PaidTestTaker mPaidTestTaker;
}
In this example a test taker delegates actually answering the
question to a paid test taker. Not ethical but a definite
example of delegation!
Follow the Law of DemeterThe Law of Demeter states (Wikipedia): An object A can request a service (call a method) of an object instance B, but object A cannot “reach through” object B to access yet another object to request its services. Doing so would mean that object A implicitly requires greater knowledge of object B’s internal structure. Instead, B’s class should be modified if necessary so that object A can simply make the request directly of object B, and then let object B propagate the request to any relevant subcomponents. If the law is followed, only object B knows its internal structure.JustificationThe purpose of this law is to break dependencies so implementations can change without breaking code. If an object wishes to remove one of its contained objects it won't be able to do so because some other object is using it. If instead the service was through an interface the object could change its implementation anytime without ill effect.CaveatAs for most laws the Law of Demeter should be ignored in certain cases. If you have a really high level object that contains a lot of subobjects, like a car contains thousands of parts, it can get absurd to created a method in car for every access to a subobject.Example
class SunWorkstation
{
public:
void UpVolume(int amount) { mSound.Up(amount); }
SoundCard mSound;
private:
GraphicsCard mGraphics;
}
SunWorksation sun;
Do : sun.UpVolume(1);
Don't: sun.mSound.Up(1);
Design by ContractThe idea of design by contract is strongly related to LSP . A contract is a formal statement of what to expect from another party. In this case the contract is between pieces of code. An object and/or method states that it does X and you are supposed to believe it. For example, when you ask an object for its volume that's what you should get. And because volume is a verifiable attribute of a thing you could run a series of checks to verify volume is correct, that is, it satisfies its contract.The contract is enforced in languages like Eiffel by pre and post condition statements that are actually part of the language. In other languages a bit of faith is needed. Design by contract when coupled with language based verification mechanisms is a very powerful idea. It makes programming more like assembling spec'd parts. Using Design by Contract
ClassesNaming Class FilesClass Definition in One FileEach class definition should be in its own file where each file is named directly after the class's name:ClassName.h Implementation in One FileIn general each class should be implemented in one source file:ClassName.cc // or whatever the extension is: cpp, c++ But When it Gets Really Big...If the source file gets too large or you want to avoid compiling templates all the time then add additional files named according to the following rule:ClassName_section.Csection is some name that identifies why the code is chunked together. The class name and section name are separated by '_'. Class LayoutA common class layout is critical from a code comprehension point of view and for automatically generating documentation. C++ programmers, through a new set of tools, can enjoy the same level generated documentation Java programmers take for granted.
Class and Method DocumentationIt is recommended a program like Doxygen be used to document C++ classes, method, variables, functions, and macros. The documentation can be extracted and put in places in a common area for all programmers to access. This saves programmers having to read through class headers. Documentation generation should be integrated with the build system where possible.
TemplatePlease use the following template when creating a new class.
/** A one line description of the class.
*
* #include "XX.h" <BR>
* -llib
*
* A longer description.
*
* @see something
*/
#ifndef XX_h
#define XX_h
// SYSTEM INCLUDES
//
// PROJECT INCLUDES
//
// LOCAL INCLUDES
//
// FORWARD REFERENCES
//
class XX
{
public:
// LIFECYCLE
/** Default constructor.
*/
XX(void);
/** Copy constructor.
*
* @param from The value to copy to this object.
*/
XX(const XX& from);
/** Destructor.
*/
~XX(void);
// OPERATORS
/** Assignment operator.
*
* @param from THe value to assign to this object.
*
* @return A reference to this object.
*/
XX& operator=(const XX& from);
// OPERATIONS
// ACCESS
// INQUIRY
protected:
private:
};
// INLINE METHODS
//
// EXTERNAL REFERENCES
//
#endif // _XX_h_
Required Methods PlaceholdersThe template has placeholders for required methods . You can delete them or implement them.Ordering is: public, protected, privateNotice that the public interface is placed first in the class, protected next, and private last. The reasons are:
LIFECYCLEThe life cycle section is for methods that control the life cycle of an object. Typically these methods include constructors, destructors, and state machine methods.OPERATORSPlace all operators in this section.OPERATIONSPlace the bulk of a class's non access and inquiry method methods here. A programmer will look here for the meat of a class's interface.ACCESSPlace attribute accessors here.INQUIRYThese are the Is* methods. Whenever you have a question to ask about an object it can be asked via in Is method. For example: IsOpen() will indicate if the object is open. A good strategy is instead of making a lot of access methods you can turn them around to be questions about the object thus reducing the exposure of internal structure. Without the IsOpen() method we might have had to do: if (STATE_OPEN == State()) which is much uglier.What should go in public/protected/private?Public SectionOnly put an object's interface in the public section. DO NOT expose any private data items in the public section. At least encapsulate access via access methods. Ideally your method interface should make most access methods unnecessary. Do not put data in the public interface.Protected and Private SectionWhat should go into the protected section versus the private section is always a matter of debate.All ProtectedSome say there should be no private section and everything not in the public section should go in the protected section. After all, we should allow all our children to change anything they wish.
All PrivateAnother camp says by making the public interface virtual any derived class can change behavior without mucking with internals.
Wishy WashyRationally decide where elements should go and put them there. Not very helpful.And the Winner Is...Keeping everything all private seems the easiest approach. By making the public methods virtual flexibility is preserved.Prototype Source File
#include "XX.h" // class implemented
/////////////////////////////// PUBLIC ///////////////////////////////////////
//============================= LIFECYCLE ====================================
XX::XX()
{
}// XX
XX::XX(const XX&)
{
}// XX
XX::~XX()
{
}// ~XX
//============================= OPERATORS ====================================
XX&
XX::operator=(const XX&);
{
return *this;
}// =
//============================= OPERATIONS ===================================
//============================= ACESS ===================================
//============================= INQUIRY ===================================
/////////////////////////////// PROTECTED ///////////////////////////////////
/////////////////////////////// PRIVATE ///////////////////////////////////
Use Header File GuardsInclude files should protect against multiple inclusion through the use of macros that "guard" the files.When Not Using Namespces#ifndef filename_h #define filename_h #endifThe new line after the endif if is required by some compilers.
When Using NamespacesIf namespaces are used then to be completely safe:#ifndef namespace_filename_h #define namespace_filename_h #endif
Required Methods for a ClassTo be good citizens almost all classes should implement the following methods. If you don't have to define and implement any of the "required" methods they should still be represented in your class definition as comments.
Justification
The Law of The Big ThreeA class with any of (destructor, assignment operator, copy constructor) generally needs all 3. For more information see http://www.parashift.com/c++-faq-lite/coding-standards.html#[25.9].ExampleThe default class template with all required methods. An example using default values:
class Planet
{
public:
/** The following is the default constructor if no arguments are supplied.
*/
Planet(int radius= 5);
// Use compiler-generated copy constructor, assignment, and destructor.
// Planet(const Planet&);
// Planet& operator=(const Planet&);
// ~Planet();
};
Method LayoutThe approach used is to place a comment block before each method that can be extracted by a tool and be made part of the class documentation. Here we'll use Doxygen which supports the Javadoc format. See the Doxygen documentation for a list of attributes supported by the document generator.
Method HeaderEvery parameter should be documented. Every return code should be documented. All exceptions should be documented. Use complete sentences when describing attributes. Make sure to think about what other resources developers may need and encode them in with the @see attributes./** Assignment operator. * * * @param val The value to assign to this object. * @exception LibaryException The explanation for the exception. * @return A reference to this object. */ XX& operator=(XX& val); Additional SectionsIn addition to the standard attribute set, the following sections can be included in the documentation:
/** Copy one string to another. * * PRECONDITION
Common Exception SectionsIf the same exceptions are being used in a number of methods, then the exceptions can be documented once in the class header and referred to from the method documentation.
Formatting Methods with Multiple ArgumentsWe should try and make methods have as few parameters as possible. If you find yourself passing the same variables to every method then that variable should probably be part of the class. When a method does have a lot of parameters format it like this:
int AnyMethod(
int arg1,
int arg2,
int arg3,
int arg4);
Different Accessor StylesWhy Accessors?Access methods provide access to the physical or logical attributes of an object. Accessing an object's attributes directly as we do for C structures is greatly discouraged in C++. We disallow direct access to attributes to break dependencies, the reason we do most things. Directly accessing an attribute exposes implementation details about the object.To see why ask yourself:
Accessors Considered Somewhat HarmfulAt least in the public interface having accessors many times is an admission of failure, a failure to make an object's interface complete. At the protected or private level accessors are fine as these are the implementation levels of a class.Implementing AccessorsThere are three major idioms for creating accessors.Get/Set
class X
{
public:
int GetAge() const { return mAge; }
void SetAge(int age) { mAge= age; }
private:
int mAge;
}
The problem with Get/Set is twofold:
One Method Name
class X
{
public:
int Age() const { return mAge; }
void Age(int age) { mAge= age; }
private:
int mAge;
}
Similar to Get/Set but cleaner. Use this approach when not
using the Attributes as Objects approach.
Attributes as Objects
class X
{
public:
int Age() const { return mAge; }
int& rAge() { return mAge; }
const String& Name() const { return mName; }
String& rName() { return mName; }
private:
int mAge;
String mName;
}
The above two attribute examples shows the strength and weakness
of the Attributes as Objects approach.
When using an int type, which is not a real object, the int is set directly because rAge() returns a reference. The object can do no checking of the value or do any representation reformatting. For many simple attributes, however, these are not horrible restrictions. A way around this problem is to use a class wrapper around base types like int. When an object is returned as reference its = operator is invoked to complete the assignment. For example: X x; x.rName()= "test";This approach is also more consistent with the object philosophy: the object should do it. An object's = operator can do all the checks for the assignment and it's done once in one place, in the object, where it belongs. It's also clean from a name perspective. When possible use this approach to attribute access.
Init Idiom for Initializing Objects
Justification
Example
class Test
{
public:
Test()
{
Init(); // Call to common object initializer
}
Test(int val)
{
Init(); // Call to common object initializer
mVal= val;
}
private:
int mVal;
String* mpName;
void Init()
{
mVal = 0;
mpName= 0;
}
}
Since the number of member variables is small, this might be better
written as:
class Test
{
public:
Test(int val = 0, String* name = 0)
: mVal(val), mpName(name) {}
private:
int mVal;
String* mpName;
};
Initialize all Variables
Justification
Minimize InlinesMinimize inlining in declarations or inlining in general. As soon as you put your C++ code in a shared library which you want to maintain compatibility with in the future, inlined code is a major pain in the butt. It's not worth it, for most cases.Think About What Work to do in ConstructorsShould you do work that can fail in constructors? If you have a compiler that does not support exceptions (or thread safe exceptions if it matters to you) then the answer is definitely no. Go directly to Do Work in Open. If your compiler supports exception then go to Do Work in Constructor. There are still reasons to use an Open method even with exceptions.Use Open Reasons
Do Work in ConstructorWith exceptions work done in the constructor can signal failure so it is fine to perform real work in the constructor. This is the guru endorced approach as a matter of fact. But there are reasons to still use an open style approach.The constructor code must still be very careful not to leak resources in the constructor. It's possible to throw an exception and not destruct objects allocated in the constructor. There is a pattern called Resource Acquisition as Initialization that says all initialization is performed in the constructor and released in the destructor. The idea is that this is a safer approach because it should reduce resource leaks.
Create an Open() method for an object which completes construction.
Open() should be called after object instantiation.
Thin vs. Thick Class InterfacesHow many methods should an object have? The right answer of course is just the right amount, we'll call this the Goldilocks level. But what is the Goldilocks level? It doesn't exist. You need to make the right judgment for your situation, which is really what programmers are for :-)The two extremes are thin classes versus thick classes. Thin classes are minimalist classes. Thin classes have as few methods as possible. The expectation is users will derive their own class from the thin class adding any needed methods. While thin classes may seem "clean" they really aren't. You can't do much with a thin class. Its main purpose is setting up a type. Since thin classes have so little functionality many programmers in a project will create derived classes with everyone adding basically the same methods. This leads to code duplication and maintenance problems which is part of the reason we use objects in the first place. The obvious solution is to push methods up to the base class. Push enough methods up to the base class and you get thick classes. Thick classes have a lot of methods. If you can think of it a thick class will have it. Why is this a problem? It may not be. If the methods are directly related to the class then there's no real problem with the class containing them. The problem is people get lazy and start adding methods to a class that are related to the class in some willow wispy way, but would be better factored out into another class. Judgment comes into play again. Thick classes have other problems. As classes get larger they may become harder to understand. They also become harder to debug as interactions become less predictable. And when a method is changed that you don't use or care about your code will still have to be recompiled, possibly retested, and rereleased. Short Methods
Justification
In a Source file Indicate if a Method is Static or VirtualIn a source file you can't tell a method is static or virtual because this information is in the header file. Knowing this information in a source file is useful and can be communicated using comments:
/*virtual*/ void
Class::method()
{
}
/*static*/ void
Class::method()
{
}
I've only seen this format once in source code, but it is interesting enough that I thought I would include here for your consideration. Notice how the method name sits alone on its own line. This looks more like C code and looks very clean for some reason.
ProcessUse a Design Notation and ProcessProgrammers need to have a common language for talking about coding, designs, and the software process in general. This is critical to project success.Any project brings together people of widely varying skills, knowledge, and experience. Even if everyone on a project is a genius you will still fail because people will endlessly talk past each other because there is no common language and processes binding the project together. All you'll get is massive fights, burnout, and little progress. If you send your group to training they may not come back seasoned experts but at least your group will all be on the same page; a team. There are many popular methodologies out there. The point is to do some research, pick a method, train your people on it, and use it. Take a look at the top of this page for links to various methodologies. You may find an Agile methodology to your liking. For more information see http://www.possibility.com/epowiki/Wiki.jsp?page=agile. You may find the CRC (class responsibility cards) approach to teasing out a design useful. Many others have. It is an informal approach encouraging team cooperation and focusing on objects doing things rather than objects having attributes. There's even a whole book on it: Using CRC Cards by Nancy M. Wilkinson. Using Use CasesA use case is a generic description of an entire transaction involving several objects. A use case can also describe the behaviour of a set of objects, such as an organization. A use case model thus presents a collection of use cases and is typically used to specify the behavior of a whole application system together with one or more external actors that interact with the system.An individual use case may have a name (although it is typically not a simple name). Its meaning is often written as an informal text description of the external actors and the sequences of events between objects that make up the transaction. Use cases can include other use cases as part of their behaviour. Requirements CaptureUse cases attempt to capture the requirements for a system in an understandable form. The idea is by running through a set of use case we can verify that the system is doing what it should be doing.Have as many use cases as needed to describe what a system needs to accomplish. The Process
Using StoriesA user story consists of just a phrase or a few sentences written down, announcing something the user wants to do. The user story is not used as a requirements specification, but as a marker for a future conversation.
A Story Is
How It All Fits TogetherThere are a lot of entrants in this part of the development process. We have user stories, use cases, task analysis, task cases, usage scenarios, etc and etc.It seems we need something more detailed than requirements as requirements tend to be fairly high level and are usually not directly useful for development. A requirement will cut across many subsystems and each of the cross-cuts must be identified. Thus requirements need to go through refinement and elaboration to be used in development. If we get too detailed then we get bogged down in detailed design before we are ready. If we aren't detailed enough then we can't know if our requirements are met. Nor can we convince ourselves that we know how to solve the problem.
User Stories Need to be Fleshed OutUser Stories are symbolic of the stuff that needs to get done. They are like the reminder notes you make to yourself. In the detailed design these can be expanded out to uses cases, to more detailed notes, state machines, etc. Or TestDrivenDesign practices could be used to flesh out the detailed implementation.For more information see http://www.possibility.com/epowiki/Edit.jsp?page=UserStory.
Unified Modeling LanguageThe Unified Modeling Language is too large to present here. Fortunately you can see it at Rational's web site. Since you do need a modeling language UML is a safe choice. It combines features from several methods into one unified language. Remember all languages and methods are open to local customization. If their language is too complex then use the parts you and your project feel they need and junk the rest.Code ReviewsIf you can make a formal code review work then my hat is off to you. Code reviews can be very useful. Unfortunately they often degrade into nit picking sessions and endless arguments about silly things. They also tend to take a lot of people's time for a questionable payback.My god he's questioning code reviews, he's not an engineer! Not really, it's the form of code reviews and how they fit into normally late chaotic projects is what is being questioned. First, code reviews are way too late to do much of anything useful. What needs reviewing are requirements and design. This is where you will get more bang for the buck. Get all relevant people in a room. Lock them in. Go over the class design and requirements until the former is good and the latter is being met. Having all the relevant people in the room makes this process a deep fruitful one as questions can be immediately answered and issues immediately explored. Usually only a couple of such meetings are necessary. If the above process is done well coding will take care of itself. If you find problems in the code review the best you can usually do is a rewrite after someone has sunk a ton of time and effort into making the code "work." You will still want to do a code review, just do it offline. Have a couple people you trust read the code in question and simply make comments to the programmer. Then the programmer and reviewers can discuss issues and work them out. Email and quick pointed discussions work well. This approach meets the goals and doesn't take the time of 6 people to do it. For more information on code reviews please take a look at here. You'll find a lot of information on justifying code reviews if you are having troubles instituting them and lots of suggestions on how to conduct them.
Create a Source Code Control System Early and Not OftenA common build system and source code control system should be put in place as early as possible in a project's lifecycle, preferably before anyone starts coding. Source code control is the structural glue binding a project together. If programmers can't easily use each other's products then you'll never be able to make a good reproducible build and people will piss away a lot of time. It's also hell converting rogue build environments to a standard system. But it seems the right of passage for every project to build their own custom environment that never quite works right.Some issues to keep in mind:
Source Code Control SystemsSome options for source code control systems are:
There are many other options out there. It doesn't matter which one you pick as much as it does that you pick one and use it.
Create a Bug Tracking System Early and Not OftenThe earlier people get used to using a bug tracking system the better. If you are 3/4 through a project and then install a bug tracking system it won't be used. You need to install a bug tracking system early so people will use it.Programmers generally resist bug tracking, yet when used correctly it can really help a project:
FYI, it's not a good idea to reward people by the number of bugs they fix :-) Source code control should be linked to the bug tracking system. During the part of a project where source is frozen before a release only checkins accompanied by a valid bug ID should be accepted. And when code is changed to fix a bug the bug ID should be included in the checkin comments.
Bug Tracking SystemsSome options for bug tracking systems are:
As for source code control systems there are many available bug tracking systems. It's more important that you use one than which one you use.
RCS Keywords, Change Log, and History PolicyWhen using RCS directly this policy must change, but when using other source code control systems like CVS that support RCS style keywords:
Justification
Create a Wiki System Early and Not OftenThe heart of software development is information. Where do you put all your documentation? Where do we put your coding guidelines? Where you put all your process documents? Where do you put your design documents? Where do you put all the 1000s of bits of information that hold a project together?With a strategy information is usually spread like seeds in the wind. Much is in people's heads, in email, in IM, in files on people's file systems, or files on different servers. Nobody can ever find anything they need. A solution to this mess is to store all your information in a wiki. A Wiki is a web site written by many users. It's very easy for a community of people to create and edit content. The idea is that people can add a page for any topic they want and add to any existing page by simply editing it. Over time we can create a site addressing most of the issues people have about projects, products, development, process, and more. A wiki is perfect support for software development. I've written up my hard one advice for using wikis at Getting Your Wiki Adopted.
Honor ResponsibilitiesResponsibility for software modules is scoped. Modules are either the responsibility of a particular person or are common. Honor this division of responsibility. Don't go changing things that aren't your responsibility to change. Only mistakes and hard feelings will result.Face it, if you don't own a piece of code you can't possibly be in a position to change it. There's too much context. Assumptions seemingly reasonable to you may be totally wrong. If you need a change simply ask the responsible person to change it. Or ask them if it is OK to make such-n-such a change. If they say OK then go ahead, otherwise holster your editor. Every rule has exceptions. If it's 3 in the morning and you need to make a change to make a deliverable then you have to do it. If someone is on vacation and no one has been assigned their module then you have to do it. If you make changes in other people's code try and use the same style they have adopted. Programmers need to mark with comments code that is particularly sensitive to change. If code in one area requires changes to code in an another area then say so. If changing data formats will cause conflicts with persistent stores or remote message sending then say so. If you are trying to minimize memory usage or achieve some other end then say so. Not everyone is as brilliant as you. The worst sin is to flit through the system changing bits of code to match your coding style. If someone isn't coding to the standards then ask them or ask your manager to ask them to code to the standards. Use common courtesy. Code with common responsibility should be treated with care. Resist making radical changes as the conflicts will be hard to resolve. Put comments in the file on how the file should be extended so everyone will follow the same rules. Try and use a common structure in all common files so people don't have to guess on where to find things and how to make changes. Checkin changes as soon as possible so conflicts don't build up. As an aside, module responsibilities must also be assigned for bug tracking purposes.
Process AutomationIt's a sad fact of human nature that if you don't measure it or check for it: it won't happen. The implication is you must automate as much of the development process as possible and provide direct feedback to developers on specific issues that they can fix.Process automation also frees up developers to do real work because they don't have to babysit builds and other project time sinks.
Automated Builds and Error AssignmentCreate an automated build system that can create nightly builds, parse the build errors, assign the errors to developers, and email developers their particular errors so they can fix them.This is the best way to maintain a clean build. Make sure the list of all errors for a build is available for everyone to see so everyone can see everyone elses errors. The goal is replace a blaim culture with a culture that tries to get things right and fixes them when they are wrong. Immediate feedback makes this possible.
Automated Code CheckingAs part of the automated build process you can check for coding standard violations and for other problems. If you don't check for it people will naturally do their own thing. Code reviews aren't good enough to keep the code correct. With a tool like Abraxis Code Check you can check the code for a lot of potential problems.This feature like the automated error assignment makes problems immediately visible and immediately correctable, all without a lot of blame and shame. Documentation ExtractionRelated to this principle is the need to automatically extract documentation from the source code and make it available on line for everyone to use. If you don't do this documentation will be seen as generally useless and developers won't put as much effort into it. Making the documentation visible encourages people to do a better job.Connect Source Code Control System and Bug Tracking System
Tools AgreementThe reality of different tool preferences is something to deal with explicitly and openly. Tools include IDEs, languages, editors, make program, source code control, bug system, debuggers, test framework, etc. Some tool decisions by their nature must be project wide, other decisions can be customized per developer.A split might also be done by who is performing the build. For example, an IDE should be able to used in local builds, but the make program would be used for nightly and release builds. Certain things are easy/trivial/useful with one tool, but hard/complicated/stupid with another tool. Unstated tool assumptions can be the source of a lot of confusion. "Get a better editor" is not always a workable response, though sometimes that's all there is to it!
Non-Blocking SchedulingSchedules are lies. Schedules suck. Yes, yes, yes. But we still need them.The most effective scheduling rule i've used is to schedule so as to unblock others. The idea is to complete the portions of a feature that will unblock those dependent on you. This way development moves along smoothly because more lines of development can be active at a time. For example, instead of implementing the entire database, implement the simple interface and stub it out. People can work for a very long time this way using that portion of the feature that caused others not to block. Plus it's a form of rapid prototyping because you get immediate feedback on these parts. Don't worry about the quality of implementation because it doesn't matter yet. Using PersonasPersonas are a powerful design tool, especially when combined with responsibility driven design. Cooper's personas are:.simply pretend users of the system you're building. You describe them, in a surprising amount of detail, and then design your system for them. I have a standard set of personas that i consider when creating a design/architecture that don't seem to be common. When you write code their are a lot of personas looking over your shoulder:
You are much more careful and more thorough when you really thing about all the personas, all the different people and all their different roles and purposes. Use a Continuous Build SystemSetup a continuous build system for your group. It should build all new check-ins and run the unit tests for each change. If you have the resources run a smoke test for each build as well.If there's a failure email should go out to the developer's who made the check-ins. This policy will allow you to catch errors as early as possible which will make your system more stable over time.
Code in the Dominant StyleLet's say someone invites you into your home as a guest. How do you act? Do you act as you do at home or do you try and fit in with the household in which you are staying? You try to fit in of course.So when you have to write code for a new codebase do you 1) write code however you want or 2) write code like the code that already exists? When writing code in a new codebase write code how the code is already written. Don't use your own style. Take some time, look around, and see how the code is written. To come in like a bull in a china shop is the mark of someone who is not a professional developer.
Run Unit Tests Before Every Check-inBefore every check-in developers must run unit tests to verify all unit tests should pass. This prevents letting any errors creep in.Finding, debugging, and handling errors is a tremendous drain on the development process so we want have a solid working system from the start. The start means your code that you check-in should always work and we know it works by running unit tests and smoke tests if your organization has them.
FormattingBraces {} PolicyBrace PlacementOf the three major brace placement strategies two are acceptable, with the first one listed being preferable:
Justification
When Braces are NeededAll if, while and do statements must either have braces or be on a single line.
Always Uses Braces FormAll if, while and do statements require braces even if there is only a single statement within the braces. For example:
if (1 == somevalue)
{
somevalue = 2;
}
JustificationIt ensures that when someone adds a line of code later there are already braces and they don't forget. It provides a more consistent look. This doesn't affect execution speed. It's easy to do.One Line Formif (1 == somevalue) somevalue = 2; JustificationIt provides safety when adding new lines while maintainng a compact readable form.Add Comments to Closing BracesAdding a comment to closing braces can help when you are reading code because you don't have to find the begin brace to know what is going on.
while(1)
{
if (valid)
{
} // if valid
else
{
} // not valid
} // end forever
Consider Screen Size LimitsSome people like blocks to fit within a common screen size so scrolling is not necessary when reading code.Indentation/Tabs/Space Policy
Justification
Example
void
func()
{
if (something bad)
{
if (another thing bad)
{
while (more input)
{
}
}
}
}
Parens () with Key Words and Functions Policy
Justification
Example
if (condition)
{
}
while (condition)
{
}
strcpy(s, s1);
return 1;
A Line Should Not Exceed 78 Characters
Justification
If Then Else FormattingLayoutIt's up to the programmer. Different bracing styles will yield slightly different looks. One common approach is:
if (condition) // Comment
{
}
else if (condition) // Comment
{
}
else // Comment
{
}
If you have else if statements then it is usually a good idea
to always have an else block for finding unhandled cases. Maybe put a log
message in the else even if there is no corrective action taken.
Condition FormatAlways put the constant on the left hand side of an equality/inequality comparison. For example:if ( 6 == errorNum ) ... One reason is that if you leave out one of the = signs, the compiler will find the error for you. A second reason is that it puts the value you are looking for right up front where you can find it instead of buried at the end of your expression. It takes a little time to get used to this format, but then it really gets useful.
switch Formatting
Example
switch (...)
{
case 1:
...
// FALL THROUGH
case 2:
{
int v;
...
}
break;
default:
}
Use of goto,continue,break and ?:GotoGoto statements should be used sparingly, as in any well-structured code. The goto debates are boring so we won't go into them here. The main place where they can be usefully employed is to break out of several levels of switch, for, and while nesting, although the need to do such a thing may indicate that the inner constructs should be broken out into a separate function, with a success/failure return code.
for (...)
{
while (...)
{
...
if (disaster)
goto error;
}
}
...
error:
clean up the mess
When a goto is necessary the accompanying label should be alone on a line and to the left of the code that follows. The goto should be commented (possibly in the block header) as to its utility and purpose. Continue and BreakContinue and break are really disguised gotos so they are covered here.Continue and break like goto should be used sparingly as they are magic in code. With a simple spell the reader is beamed to god knows where for some usually undocumented reason. The two main problems with continue are:
Consider the following example where both problems occur:
while (TRUE)
{
...
// A lot of code
...
if (/* some condition */) {
continue;
}
...
// A lot of code
...
if ( i++ > STOP_VALUE) break;
}
Note: "A lot of code" is necessary in order that the problem cannot be
caught easily by the programmer.
From the above example, a further rule may be given: Mixing continue with break in the same loop is a sure way to disaster.
?:The trouble is people usually try and stuff too much code in between the ? and :. Here are a couple of clarity rules to follow:
Example
(condition) ? funct1() : func2();
or
(condition)
? long statement
: another long statement;
One Statement Per LineThere should be only one statement per line unless the statements are very closely related.The reasons are:
One Variable Per LineRelated to this is always define one variable per line:Not: char **a, *x; Do: char** a= 0; // add doc char* x= 0; // add docThe reasons are:
Alignment of Declaration Blocks
Justification
ExampleDWORD mDword DWORD* mpDword char* mpChar char mChar mDword = 0; mpDword = NULL; mpChar = NULL; mChar = 0; Include static and virtual Key Words in Source FileIf a method is made static or virtual in the header file then also include this information as a short comment in the source file. For example:
In .h file:
class PopcornPopper
{
public:
virtual void Pop(void);
static PopcornPopper* Singleton();
};
In .cpp file:
/*virtual*/ void
PopcornPopper::Pop(void)
{
}// Pop
/*static*/ PopcornPopper*
PopcornPopper::Singleton()
{
}// Singleton
A source file doesn't contain as much information as the header file because the virtual and static keywords are dropped from method implementations. This is very useful information to have when reading code, so we want to include it in the source file.
Document Null StatementsAlways document a null body for a for or while statement so that it is clear that the null body is intentional and not missing code.
while (*dest++ = *src++)
; // VOID
Exceptions |