그래도 어느정도는 많은 사람들이 표준으로 생각하는 그런게 있는거니까, 알아둬서 나쁠건 없을듯하다! @_@
(양이 많아서 나눠서 올림..)
원문은 여기에..
http://www.possibility.com/Cpp/CppCodingStandard.html
C++ Coding Standard
Last Modified: 2002-06-08
tmh@possibility.com / My Home Page| Using this Standard. If you want to make a local copy of this standard and use it as your own you are perfectly free to do so. That's why I made it! If you find any errors or make any improvements please email me the changes so I can merge them in. Recent Changes. I also have a programmingweblog that is accidently interesting at times. |
준비되신 분은 눌러주세용...*_*
[목차]
more..
Contents
- Introduction
- Resources- Take a Look!
- Names
- Make Names Fit
- Include Units in Names
- No All Upper Case Abbreviations
- Class Names
- Class Library Names
- Method Names
- Class Attribute Names
- Method Argument Names
- Variable Names on the Stack
- Pointer Variables
- Reference Variables and Functions Returning References
- Global Variables
- Global Constants
- Static Variables
- Type Names
- Enum Names
- #define and Macro Names
- C Function Names
- C++ File Extensions
- Documentation
- Complexity Management
- Classes
- Naming Class Files
- Class Layout
- Order of public/protected/private?
- What should go in public/protected/private?
- Prototype Source File
- Use of Namespaces
- Use Header File Guards
- Required Methods for a Class
- Method Layout
- Formating Methods with Multiple Arguments
- Different Accessor Styles
- Init Idiom for Initializing Objects
- Initialize all Variables
- Minimize Inlines
- Think About What Work to do in Constructors
- Be Careful Throwing Exceptions in Destructors
- Be Const Correct
- Don't Over Use Operators
- Thin vs. Fat Class Interfaces
- Short Methods
- Process
- Use a Design Notation and Process
- Using Use Cases
- Unified Modeling Language
- OPEN Method
- Code Reviews
- Create a Source Code Control System Early and Not Often
- Create a Bug Tracking System Early and Not Often
- RCS Keyword, Change Log, and History Policy
- Honor Responsibilities
- Process Automation
- Tools Agreement
- Non-Blocking Scheduling
- Using Personas
- Using CRC Cards by Nancy M. Wilkinson
- Create a Bug Tracking System Early and Not Often
- Formatting
- Popular Myths
- Miscellaneous
- No Magic Numbers
- Error Return Check Policy
- To Use Enums or Not to Use Enums
- Macros
- Do Not Default If Test to Non-Zero
- The Bull of Boolean Types
- Usually Avoid Embedded Assignments
- Reusing Your Hard Work and the Hard Work of Others
- Use Streams
- Commenting Out Large Code Blocks
- Use #if Not #ifdef
- Mixing C and C++
- Alignment of Class Members
- Miscellaneous
- No Data Definitions in Header Files
- Make Functions Reentrant
- Error Return Check Policy
- No Magic Numbers
- Portability
more..
Introduction
Standardization is Important
It 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 Points
When a project tries to adhere to common standards a few good things happen:- Programmers can go into any code and figure out what's going on.
- New people can get up to speed quickly.
- People new to C++ are spared the need to develop a personal style and defend it to the death.
- People new to C++ are spared making the same mistakes over and over again.
- People make fewer mistakes in consistent environments.
- Programmers have a common enemy :-)
Bad Points
Now the bad:- The standard is usually stupid because it was made by someone who doesn't understand C++.
- The standard is usually stupid because it's not what I do.
- Standards reduce creativity.
- Standards are unnecessary as long as people are consistent.
- Standards enforce too much structure.
- People ignore standards anyway.
- Standards can be used as a reason for NIH (not invented here) because the new/borrowed code won't follow the standard.
Discussion
The 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.Interpretation
Conventions
The use of the word "shall" in this document requires that any project using this document must comply with the stated standard.The use of the word "should" directs projects in tailoring a project-specific standard, in that the project must include, exclude, or tailor the requirement, as appropriate.
The use of the word "may" is similar to "should", in that it designates optional requirements.
Terminology
For the sake of simplicity, the use of the word "compiler" means compiler or translator.
"C++ Coding Standard" refers to this document whereas "C++ ANSI Standard" refers to the standard C++ language definition.
Standards Enforcement
First, 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
- It's impossible.
- Maybe it's possible, but it's weak and uninteresting.
- It is true and I told you so.
- I thought of it first.
- How could it be otherwise.
6 Phases of a Project
- Enthusiasm
- Disillusionment
- Panic
- A Search for the Guilty
- The Punishment of the Innocent
- Praise and Honor for the Non-Participants
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 |
+-------------+ +------------+
Leadership
I wish i had said this, but it was said by asd@asd.com in comp.software-eng.Leaders:
- lead by example
- don't ask anything of anyone they wouldn't do themselves
- are called on to make difficult and unpopular decisions
- keep the team focused
- reward/support their team in whatever they do
- keep/clear unnecessary crap out of the way of the team
#1 " x = 1"
#2 " x != 1"
That's when a Project Leader is required. Unless you want to flip a coin.
Oh yea - 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!
- Design Stories
- OO Info Sources
- Unified Modeling Language (UML)
- OPEN Method
- OO FAQ - All You Wanted to Know About OO
- C++ FAQ LITE
- C++ Source Libraries
- C++ Tutorials
- ACE C++ Library
- Collection of Other Standards
- Design by Contract from Eiffle
- C++ isn't Perfect, Here are Some Reasons Why
- ccdoc - is a 'javadoc' like utility that extracts comments and relevant information from your C++/C programs and generates HTML pages from it.
- Const Correctness - A very nice article on const correctness by Chad Loder.
- Introduction to CRC Cards - A very nice introdction to CRC cards.
- Abraxis Code Check - A program for checking code for coding standard violations and other problems.
Names
Make Names Fit
Names 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
- Name the class after what it is. If you can't think of what it is that is a clue you have not thought through the design well enough.
- Compound names of over three words are a clue your design may be confusing various entities in your system. Revisit your design. Try a CRC card session to see if your objects have more responsibilities than they should.
- Avoid the temptation of bringing the name of the class a class derives from into the derived class's name. A class should stand on its own. It doesn't matter what it derives from.
- Suffixes are sometimes helpful. For example, if your system uses agents then naming something DownloadAgent conveys real information.
Method and Function Names
-
Usually every method and function performs an action, so the name
should make clear what it does: CheckForErrors()
instead of ErrorCheck(), DumpDataToFile() instead of DataFile().
This will also make functions and data objects more distinguishable.
Classes are often nouns. By making function names verbs and following other naming conventions programs can be read more naturally.
-
Suffixes are sometimes useful:
- Max - to mean the maximum value something can have.
- Cnt - the current count of a running count variable.
- Key - key value.
For example: RetryMax to mean the maximum number of retries, RetryCnt to mean the current retry count.
-
Prefixes are sometimes useful:
- Is - to ask a question about something. Whenever someone sees Is they will know it's a question.
- Get - get a value.
- Set - set a value.
For example: IsHitRetryLimit.
Include Units in Names
If 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
- When confronted with a situation where you could use an all upper case abbreviation instead use an initial upper case letter followed by all lower case letters. No matter what.
Justification
- People seem to have very different intuitions when making names
containing abbreviations. It's best to settle on one strategy
so the names are absolutely predictable.
Take for example NetworkABCKey. Notice how the C from ABC and K from key are confused. Some people don't mind this and others just hate it so you'll find different policies in different code so you never know what to call something.
Example
class FluidOz // NOT FluidOZ class NetworkAbcKey // NOT NetworkABCKey
Class Names
- Use upper case letters as word separators, lower case for the rest of a word
- First character in a name is upper case
- No underbars ('_')
Justification
- Of all the different naming strategies many people found this one the best compromise.
Example
class NameOneTwo class Name
Class Library Names
- Now that name spaces are becoming more widely implemented, name spaces should be used to prevent class name conflicts among libraries from different vendors and groups.
- When not using name spaces, it's common to prevent class name clashes by prefixing class names with a unique string. Two characters is sufficient, but a longer length is fine.
Example
John Johnson's complete data structure library could use JJ as a prefix, so classes would be:
class JjLinkList
{
}
Method Names
- Use the same rule as for class names.
Justification
- Of all the different naming strategies many people found this one the best compromise.
Example
class NameOneTwo
{
public:
int DoIt();
void HandleError();
}
Class Attribute Names
- Attribute names should be prepended with the character 'm'.
- After the 'm' use the same rules as for class names.
- 'm' always precedes other name modifiers like 'p' for pointer.
Justification
- Prepending 'm' prevents any conflict with method names. Often your methods and attribute names will be similar, especially for accessors.
Example
class NameOneTwo
{
public:
int VarAbc();
int ErrorNumber();
private:
int mVarAbc;
int mErrorNumber;
String* mpName;
}
Method Argument Names
- The first character should be lower case.
- All word beginnings after the first letter should be upper case as with class names.
Justification
- You can always tell which variables are passed in variables.
- You can use names similar to class names without conflicting with class names.
Example
class NameOneTwo
{
public:
int StartYourEngines(
Engine& rSomeEngine,
Engine& rAnotherEngine);
}
Variable Names on the Stack
- use all lower case letters
- use '_' as the word separator.
Justification
- With this approach the scope of the variable is clear in the code.
- Now all variables look different and are identifiable in the code.
Example
int
NameOneTwo::HandleError(int errorNumber)
{
int error= OsErr();
Time time_of_error;
ErrorProcessor error_processor;
}
Pointer Variables
- pointers should be prepended by a 'p' in most cases
- place the * close to the pointer type not the variable name
Justification
- The idea is that the difference between a pointer, object, and a reference to an object is important for understanding the code, especially in C++ where -> can be overloaded, and casting and copy semantics are important.
- Pointers really are a change of type so the * belongs near the type. One reservation with this policy relates to declaring multiple variables with the same type on the same line. In C++ the pointer modifier only applies to the closest variable, not all of them, which can be very confusing, especially for newbies. You want to have one declaration per line anyway so you can document each variable.
Example
String* pName= new String; String* pName, name, address; // note, only pName is a pointer.
Reference Variables and Functions Returning References
- References should be prepended with 'r'.
Justification
- The difference between variable types is clarified.
- It establishes the difference between a method returning a modifiable object and the same method name returning a non-modifiable object.
Example
class Test
{
public:
void DoSomething(StatusInfo& rStatus);
StatusInfo& rStatus();
const StatusInfo& Status() const;
private:
StatusInfo& mrStatus;
}
Global Variables
- Global variables should be prepended with a 'g'.
Justification
- It's important to know the scope of a variable.
Example
Logger gLog;
Logger* gpLog;
Global Constants
- Global constants should be all caps with '_' separators.
Justification
It'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
- Static variables may be prepended with 's'.
Justification
- It's important to know the scope of a variable.
Example
class Test
{
public:
private:
static StatusInfo msStatus;
}
Type Names
- When possible for types based on native types make a typedef.
- Typedef names should use the same naming policy as for a class with the word Type appended.
Justification
- Of all the different naming strategies many people found this one the best compromise.
- Types are things so should use upper case letters. Type is appended to make it clear this is not a class.
Example
typedef uint16 ModuleType; typedef uint32 SystemType;
#define and Macro Names
- Put #defines and macros in all upper using '_' separators.
Justification
This 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
- In a C++ project there should be very few C functions.
- For C functions use the GNU convention of all lower case letters with '_' as the word delimiter.
Justification
- It makes C functions very different from any C++ related names.
Example
int
some_bloody_function()
{
}
Enum Names
Labels All Upper Case with '_' Word Separators
This is the standard rule for enum labels.Example
enum PinStateType
{
PIN_OFF,
PIN_ON
};
Enums as Constants without Class Scoping
Sometimes 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 Scoping
Just name the enum items what you wish and always qualify with the class name: Aclass::PIN_OFF.Make a Label for an Error State
It'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};
Required Methods for a Class
To 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.- Default Constructor
If your class needs a constructor, make sure to provide one. You need one if during the operation of the class it creates something or does something that needs to be undone when the object dies. This includes creating memory, opening file descriptors, opening transactions etc.
If the default constructor is sufficient add a comment indicating that the compiler-generated version will be used.
If your default constructor has one or more optional arguments, add a comment indicating that it still functions as the default constructor.
- Virtual Destructor
If your class is intended to be derived from by other classes then make the destructor virtual.
- Copy Constructor
If your class is copyable, either define a copy constructor and assignment operator or add a comment indicating that the compiler-generated versions will be used.
If your class objects should not be copied, make the copy constructor and assignment operator private and don't define bodies for them. If you don't know whether the class objects should be copyable, then assume not unless and until the copy operations are needed.
- Assignment Operator
If your class is assignable, either define a assignment operator or add a comment indicating that the compiler-generated versions will be used.
If your objects should not be assigned, make the assignment operator private and don't define bodies for them. If you don't know whether the class objects should be assignable, then assume not.
Justification
-
Virtual destructors ensure objects will be completely destructed
regardless of inheritance depth. You don't have to use a
virtual destructor when:
- You don't expect a class to have descendants.
- The overhead of virtualness would be too much.
- An object must have a certain data layout and size.
- A default constructor allows an object to be used in an array.
- The copy constructor and assignment operator ensure an object is always properly constructed.
The Law of The Big Three
A 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].Example
The 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();
};
Braces {} Policy
Brace Placement
Of the three major brace placement strategies two are acceptable, with the first one listed being preferable:- Place brace under and inline with keywords:
if (condition) while (condition) { { ... ... } } - Traditional Unix policy of placing the initial brace on the
same line as the keyword and the trailing brace inline on its
own line with the keyword:
if (condition) { while (condition) { ... ... } }
Justification
- Another religious issue of great debate solved by compromise.
Either form is acceptable, many people, however, find the first
form more pleasant. Why is the topic of many psychological
studies.
There are more reasons than psychological for preferring the first style. If you use an editor (such as vi) that supports brace matching, the first is a much better style. Why? Let's say you have a large block of code and want to know where the block ends. You move to the first brace hit a key and the editor finds the matching brace. Example:
if (very_long_condition && second_very_long_condition) { ... } else if (...) { .. }To move from block to block you just need to use cursor down and your brace matching key. No need to move to the end of the line to match a brace then jerk back and forth.
When Braces are Needed
All if, while and do statements must either have braces or be on a single line.
Always Uses Braces Form
All 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;
}
Justification
It 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 Form
if (1 == somevalue) somevalue = 2;
Justification
It provides safety when adding new lines while maintainng a compact readable form.Add Comments to Closing Braces
Adding 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 Limits
Some people like blocks to fit within a common screen size so scrolling is not necessary when reading code.Indentation/Tabs/Space Policy
- Indent using 3, 4, or 8 spaces for each level.
- Do not use tabs, use spaces. Most editors can substitute spaces for tabs.
- Tabs should be fixed at 8 spaces. Don't set tabs to a different spacing, uses spaces instead.
- Indent as much as needed, but no more. There are no arbitrary rules as to the maximum indenting level. If the indenting level is more than 4 or 5 levels you may think about factoring out code.
Justification
- Tabs aren't used because 8 space indentation severely limits the number of indentation levels one can have. The argument that if this is a problem you have too many indentation levels has some force, but real code can often be three or more levels deep. Changing a tab to be less than 8 spaces is a problem because that setting is usually local. When someone prints the source tabs will be 8 characters and the code will look horrible. Same for people using other editors. Which is why we use spaces...
- When people using different tab settings the code is impossible to read or print, which is why spaces are preferable to tabs.
- Nobody can ever agree on the correct number of spaces, just be consistent. In general people have found 3 or 4 spaces per indentation level workable.
- As much as people would like to limit the maximum indentation levels it never seems to work in general. We'll trust that programmers will choose wisely how deep to nest code.
Example
void
func()
{
if (something bad)
{
if (another thing bad)
{
while (more input)
{
}
}
}
}
Parens () with Key Words and Functions Policy
- Do not put parens next to keywords. Put a space between.
- Do put parens next to function names.
- Do not use parens in return statements when it's not necessary.
Justification
- Keywords are not functions. By putting parens next to keywords keywords and function names are made to look alike.
Example
if (condition)
{
}
while (condition)
{
}
strcpy(s, s1);
return 1;
RCS Keywords, Change Log, and History Policy
When using RCS directly this policy must change, but when using other source code control systems like CVS that support RCS style keywords:- Do not use RCS keywords within files.
- Do not keep a change history in files.
- Do not keep author information in files.
Justification
- The reasoning is your source control system already keeps
all this information. There is no reason to clutter up
source files with duplicate information that:
- makes the files larger
- makes doing diffs difficult as non source code lines change
- makes the entry into the file dozens of lines lower in the file which makes a search or jump necessary for each file
- is easily available from the source code control system and does not need embedding in the file
- When files must be sent to other organizations the comments may contain internal details that should not be exposed to outsiders.
Class Layout
A 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 Documentation
It is recommended a program like ccdoc 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.
Template
Please 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=(XX& from);
// OPERATIONS
// ACCESS
// INQUIRY
protected:
private:
};
// INLINE METHODS
//
// EXTERNAL REFERENCES
//
#endif // _XX_h_
Required Methods Placeholders
The template has placeholders for required methods . You can delete them or implement them.Ordering is: public, protected, private
Notice that the public interface is placed first in the class, protected next, and private last. The reasons are:- programmers should care about a class's interface more than implementation
- when programmers need to use a class they need the interface not the implementation
LIFECYCLE
The life cycle section is for methods that control the life cycle of an object. Typically these methods include constructors, destructors, and state machine methods.OPERATORS
Place all operators in this section.OPERATIONS
Place 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.ACCESS
Place attribute accessors here.INQUIRY
These 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 Section
Only 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 Section
What should go into the protected section versus the private section is always a matter of debate.All Protected
Some 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 Private
Another camp says by making the public interface virtual any derived class can change behavior without mucking with internals.
Wishy Washy
Rationally 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.Method Layout
The 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 ccdoc which supports the Javadoc format. See the ccdoc documentation for a list of attributes supported by the document generator.
Method Header
Every 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. * * @return A reference to this object. */ XX& operator=(XX& val);
Additional Sections
In addition to the standard attribute set, the following sections can be included in the documentation:- PRECONDITION
Document what must have happened for the object to be in a state where the method can be called. - WARNING
Document anything unusual users should know about this method. - LOCK REQUIRED
Some methods require a semaphore be acquired before using the method. When this is the case use lock required and specify the name of the lock. - EXAMPLES
Include exampes of how to use a method. A picture says a 1000 words, a good example answers a 1000 questions.
/** * Copy one string to another. * * PRECONDITION
* REQUIRE(from != 0) * REQUIRE(to != 0) * * WARNING
* The to buffer must be long enough to hold * the entire from buffer. * * EXAMPLES
** strcpy(somebuf, "test") ** * @param from The string to copy. * @param to The buffer to copy the string to. * * @return void */ void strcpy(const char* from, char* to);
Common Exception Sections
If 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 Arguments
We 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);
Include Statement Documentation
Include 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:- ISA
- HASA
- USES
Example
#ifndef XX_h #define XX_h // SYSTEM INCLUDES // #includeNotice how the comments for include statements align on the third X.// standard IO interface #include // HASA string interface
Block Comments
Use 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.
Think About What Work to do in Constructors
Should 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.Do Work in Open
Do not do any real work in an object's constructor. Inside a constructor initialize variables only and/or do only actions that can't fail.Create an Open() method for an object which completes construction. Open() should be called after object instantiation.
Example
class Device
{
public:
Device() { /* initialize and other stuff */ }
int Open() { return FAIL; }
};
Device dev;
if (FAIL == dev.Open()) exit(1);
Use Open Reasons
- It is difficult to write exception safe code in constructor. It's possible to throw an exception and not destruct objects allocated in the constructor. Use of auto_ptr can help prevent this problem.
- Some compilers do not support thread safe exceptions on all platforms.
- Virtual methods are not available in base classes. If the base class is expecting a virtual method implemented by derived classes to be available during construction then initialization must follow construction. This is common in frameworks.
- Larger scale state machines may dictate when initialization should occur. An object may contain numerous other objects that may have complex initialization conditions. In this case we could wait to construct objects but then we always have to worry about null pointers.
- If deletion is needed to free resources we still may want to keep the state around for debugging or statistics or as a supplier of information for other objects.
Do Work in Constructor
With 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.
Be Careful Throwing Exceptions in Destructors
An object is presumably created to do something. Some of the changes made by an object should persist after an object dies (is destructed) and some changes should not. Take an object implementing a SQL query. If a database field is updated via the SQL object then that change should persist after the SQL objects dies. To do its work the SQL object probably created a database connection and allocated a bunch of memory. When the SQL object dies we want to close the database connection and deallocate the memory, otherwise if a lot of SQL objects are created we will run out of database connections and/or memory.The logic might look like:
Sql::~Sql()
{
delete connection;
delete buffer;
}
Let's say an exception is thrown while deleting the database connection. Will the buffer be deleted? No. Exceptions are basically non-local gotos with stack cleanup. The code for deleting the buffer will never be executed creating a gaping resource leak.
Special care must be taken to catch exceptions which may occur during object destruction. Special care must also be taken to fully destruct an object when it throws an exception.
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=(XX&);
{
return *this;
}// =
//============================= OPERATIONS ===================================
//============================= ACESS ===================================
//============================= INQUIRY ===================================
/////////////////////////////// PROTECTED ///////////////////////////////////
/////////////////////////////// PRIVATE ///////////////////////////////////
Use of Namespaces
Namespaces are now commonly implemented by compilers. They should be used if you are sure your compiler supports them completely. I don't have a lot of experience with C++ namespaces in a project setting so this section is very thin.
Naming Policy
There are two basic strategies for naming: root that name at some naming authority, like the company name and division name; try and make names globally independent.Don't Globally Define using
Don't place "using namespace" directive at global scope in a header file. This can cause lots of magic invisible conflicts that are hard to track. Keep using statements to implementation files.
Make Functions Reentrant
Functions should not keep static variables that prevent a function from being reentrant. Functions can declare variables static. Some C library functions in the past, for example, kept a static buffer to use a temporary work area. Problems happen when the function is called one or more times at the same time. This can happen when multiple tasks are used or say from a signal handler. Using the static buffer caused results to overlap and become corrupted.The moral is make your functions reentrant by not using static variables in a function. Besides, every machine has 128MB of RAM now so we don't worry about buffer space any more :-)
To Use Enums or Not to Use Enums
C++ allows constant variables, which should deprecate the use of enums as constants. Unfortunately, in most compilers constants take space. Some compilers will remove constants, but not all. Constants taking space precludes them from being used in tight memory environments like embedded systems. Workstation users should use constants and ignore the rest of this discussion.In general enums are preferred to #define as enums are understood by the debugger.
Be aware enums are not of a guaranteed size. So if you have a type that can take a known range of values and it is transported in a message you can't use an enum as the type. Use the correct integer size and use constants or #define. Casting between integers and enums is very error prone as you could cast a value not in the enum.
A C++ Workaround
C++ allows static class variables. These variables are available anywhere and only the expected amount of space is taken.Example
class Variables
{
public:
static const int A_VARIABLE;
static const int B_VARIABLE;
static const int C_VARIABLE;
}
Use Header File Guards
Include 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 Namespaces
If namespaces are used then to be completely safe:#ifndef namespace_filename_h #define namespace_filename_h #endif
-
Replace filename with the name of the file being guarded.
This should usually be the name of class contained
in the file. Use the exact class name. Some standards say use all
upper case. This is a mistake because someone could actually
name a class the same as yours but using all upper letters.
If the files end up be included together one file will prevent
the other from being included and you will be one very confused
puppy. It has happened!
-
Most standards put a leading _ and trailing _.
This is no longer valid as the C++ standard reserves leading _ to
compiler writers.
-
When the include file is not for a class then the file name
should be used as the guard name.
-
Compilers differ on how comments are handled on preprocessor directives.
Historically many compilers have not accepted comments on preprocessor
directives.
-
Historically many compilers require a new line after last endif.
A Line Should Not Exceed 78 Characters
- Lines should not exceed 78 characters.
Justification
- Even though with big monitors we stretch windows wide our printers can only print so wide. And we still need to print code.
- The wider the window the fewer windows we can have on a screen. More windows is better than wider windows.
- We even view and print diff output correctly on all terminals and printers.
If Then Else Formatting
Layout
It'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 Format
Always 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
- Falling through a case statement into the next case statement shall be permitted as long as a comment is included.
- The default case should always be present and trigger an error if it should not be reached, yet is reached.
- If you need to create variables put all the code in a block.
Example
switch (...)
{
case 1:
...
// FALL THROUGH
case 2:
{
int v;
...
}
break;
default:
}
소셜웹 반응글
'Workspace' 카테고리의 다른 글
| [C++]C++ Coding Standard - part3 (0) | 2004/12/19 |
|---|---|
| [C++]C++ Coding Standard - part2 (0) | 2004/12/19 |
| [C++]C++ Coding Standard - part1 (0) | 2004/12/19 |
| 자료구조9장 (0) | 2004/07/29 |
| 자료구조7장 (0) | 2004/07/12 |
| 자료구조6장 (0) | 2004/06/21 |