Code Formatting
Chapter 3
Braces and parenthesis
1. Braces Policy
Place braces under and inline with keywords, like this :
Example 3.1: Braces Policy Example
if(condition) while(condition)
{ {
... ...
} }
1.1. Justification
− If you use an editor (such as vi) that supports brace matching, this is a much better style than
the default unix style where braces aren’t vertically aligned. 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 3.2: Braces Policy Justification
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.
2. Braces Usage
All if, while and do statements must either have braces or be on a single line.
Always Uses Braces Form, even if there is only a single statement within the braces.
2.1. Justification
− Easier to read, you just have to scan for one form.
− Uniform idiom for scope blocks since they are all enclosed in braces.
− It provides a more consistent look.
− This doesn’t affect execution speed and it’s easy to apply.
− It ensures that when someone adds a line of code later there are already braces and they
don’t forget.
Example 3.3: Brace Usage Example
if(somevalue == 1)
{
somevalue = 2;
}
3. Parenthesis Policy
− Do put parens next to keywords.
− Do put parens next to function names.
− Do not use parens in return statements when it’s not necessary.
Example 3.4: Parenthesis Policy Example
if(condition)
{}
while(condition)
{}
strcpy(s, s1);
return 1;
Chapter 4
Class Design
1. Required Class Methods
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. If you just let the compiler generate them without indicating through
comments that you know that this is the intended behaviour, people might wonder about the
possibility of an omission or oversight.
1.1. Details
1.1.1. 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−generatedversion
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.
1.1.2. Virtual Destructor
If your class is intended to be derived from by other classes then make the destructor virtual. You
should always make a destructor virtual for the sake of future extensibility. Only make it non virtual
if you’ve got a real good reason to do so.
1.1.3. Copy Constructor
If your class is copyable, either define a copy constructor and assignment operator or add a
comment indicating that the compiler−generatedversions 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 until the copy operations are needed.
1.1.4. Assignment Operator
If your class is assignable, either define a assignment operator or add a comment indicating that
the compiler−generatedversions 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.
1.2. 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.
Making them private, prevents copies from objects being made without you knowing about it and
thus possibly inducing an unnecessary overhead or cause for inconsistency.
Example 4.1: Required Class Methods Example
class Planet
{
public:
// Planet();
Planet(int radius= 5);
~Planet();
private:
Planet(const Planet &);
Planet &operator=(const Planet &);
};
2. Initialize all Variables
You shall always initialize variables. Always. Every time.
2.1. Justification
− More problems than you can imagine are eventually traced back to a pointer or variable that
was left uninitialized. C++ tends to encourage this behaviour by propagating the initialization
to the constructors of the parent classes.
3. Accessor Styles
Accessor methods provide access to the attibutes of an object. Accessing an object’s attributes
directly, as is commonly done in C structures, is greatly discouraged in C++. It exposes
implementation details of the object and degrades encapsulation. There are two accepted ways to implement accessors, the preferable way is the following :
3.1. Attributes as objects
Example 4.2: Attributes as object accessor style
class X
{
public:
const String &name() const { return mName; }
String &rName() { return mName; }
private:
String mName;
}
The main weakness of this approach is that when returning a reference to basic types (as in the
rAge() method), it’s impossible to perform checks on the provided value. To solve this, you can
either create a wrapper class or resort to the second accessor implementation style. The preferred
method is of course, in this example, to create an Age class which contains all consistancy checks
as class methods or statements in its constructor.
The advantage of this approach is that it’s more consistent with OOP : the object should do it. An
object’s assignment (=) operator can do all the checks for assignments. This centralizes the
consistancy checks in one place, in the class, where it belongs.
When possible, use this approach instead of the "One method name" accessor style.
3.2. One method name
Example 4.3: One method name accessor style
class X
{
public:
int age() const { return mAge; }
void age(int age) { mAge = age; }
private:
int mAge;
}
Using this approach, it’s possible to include some checks about the value provided to the age()
method. However these checks make only sense when handling basic types since objects should
perform their checks internally. Failing to do so would mean that every accessor has to
reimplement these consistancy rules over and over again.
The huge drawback here is that objects aren’t treated in their own right and that encapsulation
somewhat fails. It is better to rely on the object’s assignment operator.
When possible, use this approach only for base types members (int, float, long, etc...).
Chapter 5
Class and file organization
1. Class Files
− Each class definition should be in its own file where each file is named directly after the
class’s name.
− Source files have the .cpp extension and header files have the .h extension.
− In general each class should be implemented in one source file. A common exception to this
rule are inner classes that provide class specific functionality such as thread execution.
Another common exception are very closely related classes such as a collection class and its
iterator.
− If the source file gets too large or you want to avoid compiling templates all the time then add
additional files, where the section is lower case and seperated of the classname through an
underscore.
− If the class uses inline methods, their implementation are in a separate header suffixed by
’_inline’.
1.1. Justification
− Using exactly the same name as the real class makes it easy to establish the relation.
− Not implementing several classes in one source file makes it very easy to find a class
implementation when looking for it.
Example 5.1: Class Files Example
ClassName.h
ClassName_inline.h
ClassName.cpp
ClassName_section1.cpp
ClassName_section2.cpp
2. File Header
A common file header for the whole project is important from a legal point of view and quickly find
file version.
Following is the template that should be used to organize each file header for a project using the
GPL.
Example 5.2: File header for a project using the GPL
/*
* $Id$
***
[Project Name]
**
Copyright (C) 2001,2002 [Main developers names]
* IPSquad
**
This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
**
This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
**
You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
3. Class Documentation
For the creation of developer API documentation we’re using Doxygen. Since we sometime use Qt
it’s a great asset that a documentation tool understands the concept of Qt’s meta object system,
slots and signals.
Doxygen is very much likely to javadoc, but then for C++, and more powerful.
A documentation comment is a C comment that immediately precedes a class, method, constant or
property declaration. It takes the following form:
Example 5.3: Documentation Comment Example
/**
* Documentation goes here
*/
class MyClass
{
...
The double asterisk at the start of the comment differentiates a documentation comment from a
normal comment. To make the documentation comment blocks clearly stand out, each line can be
preceded by asterisks which will be ignored when the output is generated.
The documentation is a mixture of :
3.1. Normal text
Paragraphs must be separated by at least one blank line.
3.2. Code fragments
Inline code fragments have to take the following form :
Example 5.4: Documentation inlined code fragment example
@code
.....code fragments....
@endcode
3.3. Various Doxygen tags
The tags that Doxygen understand are all in the following form and should be entered on one line :
Example 5.5: The form of Doxygen tags
@tagname [tag parameters]
3.4. List of Doxygen valid tags
The valid Doxygen tags for each type of source code entity are :
Tag Description
@brief [one_sentence] A short description of the class.
@author [one_sentence] The class’s author.
@version [once_sentence]
The class’s version. This can for example be
set to the RCS/CVS tags $Id:
cpp_codingstandard.xml,v 1.3
2002/10/07 04:57:58 ervin Exp $ or
$Revision: 1.3 $.
@return [one_sentence] A sentence describing the return value.
@exception [exception_name]
[exception_description]
Describe an exception that could be thrown by
this method.
@param [param_name]
[param_description] Describe a parameter.
3.5. Grouping members
If a class has many members, it is often desired to group them together. Doxygen already
automatically groups things together on type and protection level, but maybe you feel that this is
not enough or that that default grouping is wrong. For instance, because you feel that members of
different (syntactic) types belong to the same (semantic) group.
A member group is defined by a
//@{
...
//@}
block. Note that the members of the group should be physically inside the member group’s body.
Before the opening marker of a block a separate comment block may be placed. This block should
contain the @name command and is used to specify the header of the group. Optionally, the
comment block may also contain more detailed information about the group.
Example 5.6: Class with groups
/** A class. Details */
class Test
{
public:
/// @name First group
//@{
void func1InGroup1();
void func2InGroup1();
//@}
/** Function without group. Details. */
void ungroupedFunction();
protected:
/// @name Second group
//@{
void func1InGroup2();
void func2InGroup2();
//@}
};
4. Class Declaration
A common class declaration 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.
Following is the template that should be used to organize each class declaration.
ไม่มีความคิดเห็น:
แสดงความคิดเห็น