原文: https://wiki.openoffice.org/wiki/Writer/Code_Conventions
作者:OpenOffice.org 译者:aqcoder([email protected])
本约定为作为OppenOffice.org 代码规范的附加说明为开发者制定 cpp 代码的编写约定。约定基本有实践总结得到。
目前 OOo 代码命名规范是相当模糊的。“入乡随俗。”如果现有代码本身就是不一致将不对其做改动。本约定只做为新代码的命名约定。
- 工程目录结构:源文件必须在模块的
source/
目录下,包含头文件必须在模块的source/inc/
或者inc/
目录下。 inc/foo.hxx
或者source/inc/foo.hxx
是给模块内部使用的头文件,而inc/$PRJ_NAME/foo.hxx
是导出头文件。- 对于导出的头文件,尽量减小头文件的内部依赖,如果一个导出头文件一部分需要导出一部分内部使用,则切分这个头文件。
- 文件名只能匹配为 [a-zA-Z][a-za-z0-9.-]*,且头文件/源文件的扩展名为 hxx/cxx。类名推荐使用驼峰法。
- 命名空间全为小写。
namespace com { namespace sun { namespace star { }}}
有些老的模块使用前缀来代替命名空间,当在其加入新代码时使用包含在内的命名空间,不要新建模块。
namespace sw { namespace newnamespace {}}
- 使用大写前缀驼峰法命名类名
namespace sw
{
class SomeClassExample;
}
在 sw 模块,在全局命名空间的类需要加前缀 Sw
class SwSomeClassExample;
- 接口以 I 为前缀,接口必须是纯虚基类。
namespace sw
{
class ISomeInterfaceExample;
}
方法使用小写前缀驼峰法或者大写前缀驼峰法,通常以动词开头。
void SomeClassExample::appendChild();
void fixUrlHeaders();
注意,甚至缩写通常大写都只有第一个字母大写。如果一个类没有遵循这个惯例,使用旧的惯例或更新类是完全符合本约定的。
- 关于方法名使用大写前缀驼峰法还是小写驼峰法:当实现 UNO 接口时类的方法使用小写前缀驼峰法。
- 本地方法使用静态函数,并且定义在匿名命名空间中,以 lcl_ 做为前缀。
- 函数返回布尔值需要以回答问题的方式(译者注:应该是指一些成员函数)
bool hasChild();
bool isLeaf();
- 局部变量使用匈牙利命名法,用个小写字母做为前缀,紧跟着大写前缀名称。
const sal_Int32 aParameterName;
const OUString& rParameterByReference;
前缀 | 含义 |
---|---|
r | 引用 |
x | UNO 引用 |
p | 指针(包括 STL 风格的迭代器和智能指针,UNO 引用除外) |
a | 一些值 |
有些值类型使用特定前缀而不是使用 a
前缀 | 含义 |
---|---|
c | 字符型 |
e | 枚举类型 |
f | 浮点型 |
n | 整型 |
s | 字符串型 |
v | 容器 (vector) |
如果一个方法没有遵循本约定,保持原来的约定或者更新到现有约定。 下面给出一些好的/坏的匈牙利符号
-
http://herbsutter.wordpress.com/2008/07/15/hungarian-notation-is-clearly-goodbad/
-
布尔类型变量需要使用回答的形式,使用 is/has 作为匈牙利前缀。
bool isLeaf;
bool hasChild;
参数命名遵从的规则和局部变量一致,但是有一些附加前缀:
前缀 | 含义 |
---|---|
i_ | 传入参数 |
o_ | 传出参数 |
io_ | 传入传出 |
如果函数没有遵从本约定,则保留原来的约定或者都改为遵从本约定。
成员变量命名遵从局部变量的约定,但是需要加上前缀 ‘m_’。
SwPosition m_aPosition;
Node const * const m_pParent;
如果函数没有遵从本约定,则保留原来的约定或者都改为遵从本约定。
成员变量命名遵从局部变量的约定,但是需要加上前缀 ‘our_’。
static ::osl::Mutex our_aFileMutex;
如果函数没有遵从本约定,则保留原来的约定或者都改为遵从本约定。
- 不要使用预处理宏代替常量,尽量不要使用全局常量。
- 如果可能的话尽量使用 static const 修饰常量。
- 在 cxx 文件中,常量成员放到匿名命名空间中。(不要将他们放到头文件中)
namespace
{
static const OUString sLogging = OUString::createFromAscii("com.sun.star.logging");
}
typedefs 必须以 _t 或者 _type 结尾。
class MyClass
{
typedef ::rtl::OUString string_t;
typeded sal_Int32 element_t;
};
模板参数必须以大写字母 t 开始,紧接着小写开头单词:
template<typename Tparam> class MyTemplateClass {};
命名前缀规则:
<scope-prefix>_<hungarian-prefix>VariableName;
scope-prefix 是如下其中一个:
前缀 | 含义 |
---|---|
局部变量 | |
i_ | 传入参数 |
o_ | 传出参数 |
io | 传入传出参数 |
m_ | 成员变量 |
our_ | 金泰成员变量 |
g_ | 全局变量 |
lcl_ | 文件局部成员函数或变量 |
匈牙利前缀参考上文所述。
Makefile 中的列表您可能的一个入口占一行,且按字母顺序排列,这样容易合并。
SLOFILES= \
$(SLO)$/config.obj \
$(SLO)$/corecontroller.obj \
$(SLO)$/errormail.obj \
$(SLO)$/invitejob.obj \
$(SLO)$/logpacker.obj \
$(SLO)$/logstorage.obj \
$(SLO)$/myconfigurationhelper.obj \
$(SLO)$/onlogrotatejob.obj \
$(SLO)$/oooimprovement_exports.obj \
$(SLO)$/soaprequest.obj \
$(SLO)$/soapsender.obj \
- cpp 文件按如下顺序布局
- license
- includes
- defines (if and)
- global using statements
- anonymous namespace with local functions and data(implementation files only)
- anything else
- 尽量保持每行少于 80 个字符(否则你就要考虑重构了)。在如下条件下可以忽略这条约定:
- 头文件声明不允许,类型名很长。
- 在子类声明中罗列基类的虚函数时不允许换行。
- 定义字符串常量。
- 断言字符串,等。
如果对可读性没有帮助,在这些情况下不要断行,记住:字符串常量可以断行:
static const SOME_STRING = ::rtl::OUString::createFromAscii(
"Libertatem quam peperere maiores digne studeat servare posteritas.\n"
"means\n"
"May the freedom won by our forefathers be conserved in dignity for posterity.\n");
-
缩进方法遵循 Allman-Style
-
if 判断时,语义通顺且短,可以放在一行中:
if(!pData) return;
但是要注意即使语句很短,在调试的时候换行对设置断点是很有用的。所以尽量不要在 if 语句中使用一行。
- 在一份声明中如果使用换行(如果 if 语句, 参数列表),不使用空间对齐语句。其不良维护。简单的缩进线继续发表声明就可以了:
Reference<XInterface> MyConfigurationHelper::openConfig(
const Reference<XMultiServiceFactory> xSMGR,
const OUString& sPackage,
sal_Int32 eMode)
{ }
当在一个列表(如参数列表或 for 语句)使用换行,使每条语句为一行。
- 代码注释中不要包含时间戳("created on", "last modified by"),因为他们是无效的。在 bug/issues 中注明修改,即使只有简单的几行说明,这是为了方便问题回归。
-
在源文件中不要出现交叉包含。例如:sw/source/ui 下的源文件只能包含 sw 模块的全局包含目录 sw/inc 和 ui 下的包含目录 /sw/source/ui/inc。他不能包含项 /sw/source/core/inc 和 sw/source/filter/inc 这样的其他目录下的头文件。
-
保证成员变量都是私有的或者是受保护的。如果需要的话生命内联属性器方法 getter/setter。属性器的名称是成员变量的名称去除前缀。如果一定要定义共有的成员请使用结构体关键字 struct 代替 class。
class SomeExampleClass
{
public:
::rtl::OUString getName();
void setName(const OUString& rName);
private:
::rtl::OUString m_aName;
};
struct AnotherExampleClass
{
public:
::rtl::OUString m_aName;
};
- 函数实现不要超过 200 行(超过的话请重构)
- 如果可能的话使用块限制局部变量的范围。一个局部变量应该很少超过一个屏幕的长度(30 - 60 LOC)。
- 保证头文件的可读性 ** 在头文件中使用 handle/body(pimpl) 或者虚基类来屏蔽具体的实现细节。使用 boost::scoped_ptr<>(详见 和 ) 代替纯指针,因为具体实现不需要暴露在头文件中。 ** 尽量避免在头文件中定义私有的成员函数。如果实现函数只需访问一些成员对象,在匿名命名空间中实现这些函数。如果需要访问太多的成员对象,请重构。
namespace
{
static sal_Int32 lcl_countLines(const Sequence<sal_Int8>& rBuffer)
{
...
};
}
-
尽量避免暴露纯指针,使用 com::sun::star::uno::Reference 引用 UNO 对象,boost::shared_ptr<> 引用其他对象。如果需要纯指针就使用 shared_ptr<>.get()。无论何时都要保证访问的指针是有效的。很多时候使用 boost::weak_ptr<> 都是使用纯指针好。
-
断言:在 OOo 中有很多种断言,通常都是一些宏定义: ** OSL_ENSURE 只有在 debug 下才有效。 ** DBG_* 是模块工具定义的。所以(therefore evil by definition?)
-
use mutable and explicit in new code
-
在新代码里使用英文注释,不要在同一个方法中混合德语和英语,如果重构一个模块,把注释翻译成英语。
-
不要使用 C-style 的类型转换,使用 C++-style 的类型转换。
-
提供纯虚函数作为接口。
-
Do not link against specific implementations, link against interfaces.
-
使用虚共有继承实现接口。
-
在纯虚基类中使用宏 SAL_NO_VTABLE:
struct SAL_NO_VTABLE IMyInterface
{
virtual void myMethod() = 0;
};
Use SALDLLPRIVATE and SAL_DLLPUBLIC* for large shared libraries that are not only exporting the few UNO component access functions. See e.g. svx/svxdllapi.h for a usage example.
- 在非 UNO 组件的共享库中使用 SALDLLPRIVATE 和 SAL_DLLPUBLIC* 指定导出,参考例子 svx/svxdllapi.h。
不宜使用的类型(已经被 OpenOffice.org 编码规范覆盖)
-
不要使用 /svtools/svarray.h 或 /svtools/svstdarr.hxx 中的容器,或类似的代码。使用 STL 的容器。
-
避免使用 tools/rtti.hxx 中的 rtti(运行时类型识别),使用 C++ 内置的 RTTI(type_info, dynamic_cast, typeid)。
-
不要使用 "tools/contnr.hhx", "tools/table.hxx", "tools/stack.hxx", "tools/queue.hxx", "tools/dynary.hxx" 中的容器。使用 STL 代替。
-
不要在新代码中使用 ByteString 或者 std::string,使用 OUString 代替。
-
不要在新代码中使用 String,使用 OUString 代替。
-
不要在新代码中使用 BOOL 或者 FASTBOOL,使用 bool 代替,在 UNO 中使用 sal_Bool。
-
其他:如果 UNO 类型可用,使用 UNO 类型。