全部博文(41)
分类: C/C++
2010-07-01 14:46:56
doxygen
[功能]
为许多种语言编写的程序生成文档的工具。
[举例]
*生成一个模板配置文件,模板文件中有详细的注释:
$doxgen -g test
这样,会生成一个test文件,1500多行,可以把这个文件做为模板编写配置文件。如果之前有test那么会将原来的test备份为test.bak.模板文件的部分内容如下:
...前面的内容省略...
DOXYFILE_ENCODING = UTF-8
# The PROJECT_NAME tag is a single word (or a sequence of words surrounded
# by quotes) that should identify the project.
PROJECT_NAME =
# The PROJECT_NUMBER tag can be used to enter a project or revision number.
# This could be handy for archiving the generated documentation or
# if some version control system is used.
PROJECT_NUMBER =
# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
# base path where the generated documentation will be put.
# If a relative path is entered, it will be relative to the location
# where doxygen was started. If left blank the current directory will be used.
OUTPUT_DIRECTORY =
...后面的内容省略...
*生成一个模板配置文件,模板文件中只有简单的注释:
$doxygen -s -g test
这里,我尝试并且比较过,如果使用生成的文件只200多行,几乎没有注释,只有几行分类的注释,去除了多余的注释。
*用当前版本的doxygen更新使用旧版本doxygen生成的配置文件:
$doxygen -u test
这里,配置文件是test.这个命令我没有实践过,在/usr/share/doc/doxygen的doxygen_manual.pdf文档的Doxygen usage节说:如果配置文件是旧版本的doxygen生成的,则需要用当前版本的doxygen对这个配置文件进行更新。
*使用test文件里的配置,生成源代码的文档:
$doxygen test
这里,假设test就在当前目录,在test里面已经配置指定了源文件的位置,以及生成文档的位置等。为简单起见,列出一些重要的配置选项,其他的参见具体生成的配置模板,配置文件中部分选项取值如下:
############配置文件的部分内容#############
#给出所有文档的输出目录
OUTPUT_DIRECTORY = doc
#设置使用的语言
OUTPUT_LANGUAGE = Chinese
#生成chm格式的压缩html文档
GENERATE_HTMLHELP = YES
#生成latex文档
GENERATE_LATEX = YES
#生成rtf文档
GENERATE_RTF = YES
#指定doxygen分析的输入文件(目录)
INPUT = src lib
#指定分析的文件的类型(扩展名)
INCLUDE_FILE_PATTERNS = *.cpp *.h
#递归查找INPUT中的文件
RECURSIVE = YES
#处理完一个函数的文档之后,对函数调用的函数也列出相关的链接。
REFERENCES_RELATION = YES
#INCLUDE_PATH = #这个不需要改
[描述]
doxygen可以为c,c++,java等语言写的程序生成文档(从程序的源代码中提取其中按照约定格式写的注释中提取信息)。查看doxygen的man手册,翻译如下:
许多使用doxygen的方法:
1)使用doxygen生成一个配置文件的模板:
doxygen [-s] -g [configName]
如果configName是'-'那么doxygen将会把结果写到标准输出。
2)使用doxygen更新旧的配置文件:
doxygen [-s] -u [configName]
3)根据已经存在的配置文件,使用doxygen生成文档:
doxygen [configName]
如果configName是'-'那么doxygen将会从标准输入读取配置信息。
4)使用doxygen生成RTF,HTML,或者Latex风格的模板文件??:
RTF格式:doxygen -w rtf styleSheetFile
HTML格式:doxygen -w html headerFile footerFile styleSheetFile [configFile]
LaTex格式:doxygen -w latex headerFile styleSheetFile [configFile]
5)使用doxygen生成rtf扩展文件??
RTF格式:doxygen -e rtf extensionsFile
如果指定了-s那么配置文件中的注释将会被忽略。如果配置名被忽略了,那么'Doxy-file'将被做为默认的文件使用。
安装doxygen-doc会获得如何使用doxygen的更多信息(即$sudo apt-get install doxygen-doc)。可以查看/usr/share/doc/doxygen.
[其他]
*安装doxygen
===============================
$sudo apt-get install doxygen
这样会安装上doxygen。
*doxygen配置文件
===============================
doxygen根据配置文件中指定的选项生成相应配置的文档。doxygen配置文件的格式是也是通常的unix下配置文件的格式:注释'#'开始;tag = value [,value2…];对于多值的情况可以使用 tag += value [,value2…]。
对doxygen的配置文件的修改分为两类:一种就是输出选项,控制如何解释源代码、如何输出;一种就是项目相关的信息,比如项目名称、源代码目录、输出文档目录等。对于第一种设置好后,通常所有项目可以共用一份配置,而后一种是每个项目必须设置的。下面选择重要的,有可能需要修改的选项进行解释说明,其他选项在配置文件都有详细解释。
TAG 缺省值 含义
PROJECT_NAME 项目名称
PROJECT_NUMBER 可以理解为版本信息
OUTPUT_DIRECTORY 输出文件到的目录,相对目录(doxygen运行目录)或者绝对目录
INPUT 代码文件或者代码所在目录,使用空格分割
FILE_PATTERNS *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx *.hpp *.h++ *.idl *.odl 指定INPUT的目录中特定文件,如:*.cpp *.c *.h
RECURSIVE NO 是否递归INPUT中目录的子目录
EXCLUDE 在INPUT目录中需要忽略的子目录
EXCLUDE_PATTERNS 明确指定的在INPUT目录中需要忽略的文件,如:FromOut*.cpp
OUTPUT_LANGUAGE English 生成文档的语言,当前支持2、30种语言,国内用户可以设置为Chinese
USE_WINDOWS_ENCODING YES(win版本)
NO(unix版本) 编码格式,默认即可。
EXTRACT_ALL NO 为NO,只解释有doxygen格式注释的代码;为YES,解析所有代码,即使没有注释。类的私有成员和所有的静态项由EXTRACT_PRIVATE和 EXTRACT_STATIC控制
EXTRACT_PRIVATE NO 是否解析类的私有成员
EXTRACT_STATIC NO 是否解析静态项
EXTRACT_LOCAL_CLASSES YES 是否解析源文件(cpp文件)中定义的类
SOURCE_BROWSER NO 如果为YES,源代码文件会被包含在文档中
INLINE_SOURCES NO 如果为YES,函数和类的实现代码被包含在文档中
ALPHABETICAL_INDEX NO 生成一个字母序的列表,有很多类、结构等项时建议设为YES
GENERATE_HTML YES 是否生成HTML格式文档
GENERATE_HTMLHELP NO 是否生成压缩HTML格式文档(.chm)
GENERATE_LATEX YES 是否乘车latex格式的文档
GENERATE_RTF NO 是否生成RTF格式的文档
GENERATE_MAN NO 是否生成man格式文档
GENERATE_XML NO 是否生成XML格式文档
*doxygen在处理注释文档的时候,会进行如下的过程:
===============================
1)执行在文档中的特殊命令。命令都以'\'或'@'开始,二者是一样的,都是为了引出一个命令。例如'\brief','\details','@brief', '@details'等等。
2)如果一行以空白开始,后面跟着一个或者多个'*',然后又跟着0个或者多个空白,那么所有的空白和'*'将会被移除。
3)所有的空行将会被当作段分隔符号。
4)为相应的被文档化了的类的单词创建相应的链接(除非这个单词前面放置一个'%',这时候就没有链接了并且'%'也会被移走了)。
5)如果在文本中发现了相应的匹配,会建立成员的链接。(?)
6)文档中的HTML标记会被解释、转化为LATEX的东西以便成为LATEX的输出。(?)
*注释文档在代码中可以放置的位置
===============================
1)在声明或者定义的成员、类、命名空间前面添加文档块。
2)将文档块放在其他的位置(有可能是不同的文件或者同一个文件的其他位置),用一个和结构相关的命令将这个文档块和相应被文档化的地方链接起来。
另外,doxygen也允许把注释放到代码后面,具体格式是放一个'<'到注释开始部分。例如:
int var1 ;
int var2; ///< ….text….
这里,第2)个情况把文档注释块放到其他位置,对于注释一个文件来说只能用这种方法。但是一般情况不推荐用这种方式,建议注释尽量放在代码前后。
*文档描述的种类
===============================
注释生成的文档描述一般有两种,一个是简短描述用于描述大致信息,一个详细描述描述更为具体信息。
*将注释块标记为详细描述的方法:
1)JavaDoc风格,它的注释是c风格的,并且打开注释后再加上一个'*',例如:
/**
* ... text ...
*/
2)Qt风格,它的注释是c风格的,并且打开注释后加上一个'!',例如:
/*!
* ... text ...
*/
在这两种方式中,中间的'*'是可选择的,所以类似
/*!
... text ...
*/
的注释也是合法的。
3)C++风格,它的注释是C++风格的,并且打开注释的每行额外添加一个'/'或者'!',例如:
///
/// ... text ...
///
或者
//!
//!... text ...
//!
*将注释块标记为简短描述的方法:
1)可以在前面的注释块(详细描述的注释块)中使用'\brief'命令。这个命令将一个段落结束标记作为它的结束,所以后面详细的描述前面应该有一个空行。例如:
/*! \brief Brief description.
Brief description continued.
*
*
* Detailed description starts here.
*/
2)如果在配置文件中设置JAVADOC AUTOBRIEF为YES,那么JavaDoc风格将会自动开始一个简短的描述,这个简短的描述以第一个遇到的'.'加上一个空格或者换行符号作为它的结束。例如:
/** Brief description which ends at this dot. Details follow
* here.
*/
这个项对C++风格的注释也有同样的效果。例如:
/// Brief description which ends at this dot. Details follow
/// here.
3)使用C++风格的注释,这个注释不会跨越多行。例如:
/// Brief description.
/** Detailed description. */
或者
//! Brief descripion.
//! Detailed description
//! starts here.
注意这里的最后一种情况,详细描述(Detailed description)和简短描述(Brief description)之间要有一个空行来分隔,实践发现好像句点不是必须的。对于这种情况,JAVADOC AUTOBRIEF 也可以设置为NO.
*doxygen是风常灵活的,如果你有许多详细的描述,例如下面这样:
//! Brief description, which is
//! really a detailed description since it spans multiple lines.
/*! Another detailed description!
*/
那它们将会被合并。即使它们在代码中不同的地方也会被合并,这时候次序将取决于doxygen处理代码的次序。
*关于列表
===============================
可以使用生成列表的文档的注释:
主要两种方式:
1)使用'-'。2)使用HTML命令
这里说地一种方式,比较简单。
通过在每行的开头添加一系列列对齐的减号('-'),可以生成一个列表。列表项也可以具有标号,这需要在减号的后面跟上一个'#'。列表也可以是嵌套的,这取决于列表项的缩进方式。注意不要忘记'-#'后面的空格。
这里给出一个例子:
/*!
* A list of events:
- mouse events
*
-# mouse move event
*
-# mouse click event\n
*
More info about the click event.
*
-# mouse double click event
*
- keyboard events
*
-# key down event
*
-# key up event
*
*
* More text here.
*/
上面的注释,生成的文档结果会显示如下:
A list of events:
• mouse events
1. mouse move event
2. mouse click event
More info about the click event.
3. mouse double click event
• keyboard events
1. key down event
2. key up event
More text here.
如果你使用[Tab]键来标识嵌套列表的缩进,那么一定要确保配置中的TAB SIZE设置的是正确的。你可以使用一个新段(空行)来结束列表,或者使用一个具有一定层次缩进的只包含'.'的行,来结束相应的列表。例如:
/**
* Text before the list
* - list item 1
- sub item 1
*
- sub sub item 1
*
- sub sub item 2
*
.
*
The dot above ends the sub sub item list.
*
More text for the first sub item
*
.
*
The dot above ends the first sub item.
*
More text for the first list item
*
- sub item 2
*
- sub item 3
*
* - list item 2
* .
* More text in the same paragraph.
*
* More text in a new paragraph.
*/
*关于分组(模块)
===============================
doxygen有三种机制用于将文档分组。"module","member groups","subpaging".
1,module
module可以把东西分组组织到同一个页中。 这样组中的成员可以是文件,命名空间,类,函数,变量,枚举,typedef,宏定义,以及其它的组。
1.1)'\defgroup'
在文档注释块中使用'\defgroup'命令来定义一个组。命令有两个参数,第一个参数用来唯一标识组,第二个参数是指明该组在文档中显示的标题。例如:
/** @defgroup group1 The First Group
* This is the first group
* */
这里,@defgroup和\defgroup是一样的。使用了这个命令之后,这将导致生成的文档的起始页(假设是html的)多一个“模块”的标签。
1.2)'\ingroup'
你可以使用'\ingroup'命令将某一个实体指明为一个组的成员。例如:
/** @defgroup group2 The Second Group
* This is the second group
*/
/**
* @ingroup group2
* @brief class C3 in group 2
*/
class C3 {};
这里先定义了一个group2,然后给一个类添加注释,指明这个类属于group2.
还:
/** @file
* @ingroup group3
* @brief this file in group 3
*/
还:
/** @defgroup group4 The Fourth Group
* @ingroup group3
* Group 4 is a subgroup of group 3
*/
1.3)'@{'开始,'@}'结束的标记对儿
如果不用'\ingroup'命令,你可以在组的后面加上'@{'开始,'@}'结束的对儿,将其中的成员一起指定为该组。例如:
/** @defgroup group1 The First Group
* This is the first group
* @{
*/
/** @brief class C1 in group 1 */
class C1 {};
/** @brief class C2 in group 1 */
class C2 {};
/** function in group 1 */
void func() {}
/** @} */ // end of group1
这里,标记对儿中的没被文档注释的变量会列到文档中,不过没有相应该成员的文档描述链接了。
这样的对儿可以在组定义的文档中,也可以在独立的文档块中。例如:
* @{
*/
/** a function in group */
void func2() {}
/** yet another function in group */
void func3() {}
/** @} */ // end of group
这样行么?
每个组也可以嵌入到这样的对儿中。
1.4)'\addtogroup'
如果你使用同一个组名一次以上那么你会遇到一个错误。你可以使用'\addtogroup'来代替'\defgroup'来防止doxygen只限制唯一的标识。它的用法和'\defgroup'是一样的,不同的只是当多次使用一个组名的时候,它会自动将其中的内容和之前的组名合并。组的题目在这里是可选的,语法如下,
/** \addtogroup
/*\@{*/
/*\@}*/
例如:
/** @addtogroup group1
*
* More documentation for the first group.
* @{
*/
/** another function in group 1 */
void func2() {}
/** yet another function in group 1 */
void func3() {}
/** @} */ // end of group1
注意:组合实体(例如classes, files 以及 namespaces)可以放到多个组中,但是成员(例如variable, functions, typedefs 和 enums) 只能被放到一个组中,这样是为了防止歧义。
1.5)高优先权取代低优先权防止歧义
doxygen会将较高优先级定义的member放到组中。例如显示的'\ingroup'会取代隐式的组定义'@{'和'@}'.举例:
/**
* \ingroup A
*/
extern int VarInA;
/**
* \defgroup IntVariables Global integer variables
*/
/*@{*/
/** an integer variable */
extern int IntegerVariable;
/*@}*/
....
/**
* \defgroup Variables Global variables
*/
/*@{*/
/** a variable in group A */
int VarInA;
int IntegerVariable;
/*@}*/
这里,VarInA;会放在组A中,而不是组Variables。IntegerVariable在组IntVariables中而不是组Variables,因为在组Variables没有给这个变量添加文档注释。
1.6)'\ref'
引用一个组,使用'\ref'命令。'\ref'命令的第一个参数是组标识(或者页标识),如果你想要使用自定义的连接名字,那么可以在组标识的后面把自定义的连接名字用双引号引起来。例如:
//!This is the \ref group_label "link" to this group.
组定义的优先级,从高到低,依次为:\ingroup, \defgroup, \addtogroup,\weakgroup。这里,最后一个命令和\addtogroup一样,不过优先级别比较低。
2,membergroups
成员组一般是对类等组合类型的成员进行分组,所以一般它们是在类内进行的。在文档页中进入这个类的相关文档之后,才会看到该类的member group.
member group的定义如下:
//@{
...
//@}
如果你喜欢c风格的注释,可以定义如下:
/*@{*/
...
/*@}*/
组的成员必须在物理位置上面在组的‘包含体’内。
在这样的标记块之前,还需要有一个文档注释块,这个注释块包含 @name (或者 \name) 命令用来指定组的头部,当然它也可以包含更多关于组的信息。
在membergroups中不允许嵌套!!
成员组使用举例:
/** @name Group2
* Description of group 2.
*/
//@{
/** Function 2 in group 2. Details. */
void Test::func2InGroup2() {}
/** Function 1 in group 2. Details. */
void Test::func1InGroup2() {}
//@}
如果类中的一个member group中的所有成员具有同样的类型和保护级别,那么整个member group将会属于一个类型/保护级别的子组。例如,一个member group的所有member是公有的静态的member那么将会在文档中成为”Static Public Members”的子段。如果两个或者更多的members具有不同的类型或者保护级别,那么这个组将会和自动生成的组处于同一个层次。如果你想要让所有的member group在顶层,那么你需要使用'\nosubgrouping'命令。
3,subpaging
信息可以使用'\page'和'\mainpage'命令被组织成页。这样一般会导致一个“扁平的”页的列表,并且'main'页在列表的第一个位置。
'\page'命令的语法:
\page
举例:
//!\page page1 第一个页
这里,这个命令将导致文档主页上方的标签多一个相关页面的链接。“第一个页”将会显示其中.
'\mainpage'命令的语法:
\mainpage [(title)]
举例:
//!\mainpage 这是程序的主页
//!程序的主运行文件,程序的功能是测试doxygen如何生成文档.
这里,这个命令将导致标签页上面的“主页”上的内容是‘程序的主运行文件,程序的功能是测试doxygen如何生成文档.’,不使用这个命令,那么主页的内容是空白的。注意这个命令后面不能有空行。否则空行后面的注释都无法加入到文档当中了。
'\subpage'命令的语法:
\subpage
举例:
假设前面我用'\mainpage'之后,紧接这指定mainpage的子页为page1和page2那么如下:
//!\mainpage 这是程序的主页
//!程序的主运行文件,程序的功能是测试doxygen如何生成文档.
//!- \subpage page1 第一个子页\n
//!第一个页,使用subpage命令在mainpage中指定.
//!- \subpage page2 第二个子页
这里,我使用列表标记'-'将subpage分成两个列表了。这样效果是,点击文档中的“主页”标签,将会显示:
这是程序的主页
程序的主运行文件,程序的功能是测试doxygen如何生成文档.
* 第一个用page命令的页 第一个子页
第一个页,使用subpage命令在mainpage中指定.
* 第二个页面 第二个子页
对于一个页A,'\subpage'命令给另外一个页B添加一个链接并且让页B成为页A的子页。这样的效果就是创建了两个group GA和GB,这里GB是GA的一部分,页A被放到GA中,页B被放到GB中。
*一个完整的使用例子:
===============================
这个程序主要用来测试doxygen。使用了doxygen的最简单的注释功能。
程序代码结构是
./src/main.cpp
./src/subModule/myClass.h
./src/subModule/myClass.h
./src/main.cpp
./lib/say.h
./lib/say.cpp
其中main.cpp调用myClass.h中的类;myClass.cpp中的函数调用say.h中的类,并打印返回值。say.cpp中的函数打印"hello"字符串。
生成的文档步骤如下:
1)安装doxygen:
$sudo apt-get install doxygen
2)生成doxygen提取文档的选项配置文件:
$doxygen -g test
3)修改配置文件:
#给出所有文档的输出目录
OUTPUT_DIRECTORY = doc
#设置使用的语言
OUTPUT_LANGUAGE = Chinese
#生成chm格式的压缩html文档
GENERATE_HTMLHELP = YES
#生成latex文档
GENERATE_LATEX = YES
#生成rtf文档
GENERATE_RTF = YES
#指定doxygen分析的输入文件(目录)
INPUT = src lib
#指定分析的文件的类型(扩展名)
INCLUDE_FILE_PATTERNS = *.cpp *.h
#递归查找INPUT中的文件
RECURSIVE = YES
#处理完一个函数的文档之后,对函数调用的函数也列出相关的链接。
REFERENCES_RELATION = YES
#INCLUDE_PATH = #这个不需要改
这里,搜索test中的含有'lvkai'的注释部分。
4)根据配置文件生成文档:
$doxygen test
5)产生的文档在doc文件夹中,html的index.html中将包含"首页"、"类"、"文件"三个标签。
这里,对每个类进行了注释,具体的注释格式不多说了,需要参见文档。省城的文档将在./doc目录中。重点对MyClass类进行注释,所以可看看它的源代码是怎么注释的。详细的源代码如下:
./Makefile内容如下:
app:src/main.cpp src/subModule/myClass.cpp lib/say.cpp
g++ src/main.cpp src/subModule/myClass.cpp lib/say.cpp -o app
./src/main.cpp内容如下:
//!程序的主运行文件,程序的功能是测试doxygen如何生成文档.
#include
#include "subModule/myClass.h"
using std::cout;
using std::endl;
int main(int argc, char *argv[])
{
cout<<"Begin "<<__FILE__<<":"<<__FUNCTION__<
MyClass my;
my.run();
cout<<"End "<<__FILE__<<":"<<__FUNCTION__<
return 0;
}
./src/subModule/myclass.h内容如下:
#ifndef __MYCLASS_H
#define __MYCLASS_H
//下面的注释用于doxygen文档是简短的注释后面接一个空行,然后是详细的注释。
//doxygen的注释这里用两个斜线加一个感叹号组成。
//!运行程序的主调用接口类.
//!这个类是运行时候主程序直接调用的类,然后这个类执行的时候会调用其他的类。
//!这个例子里面,其他的类就是Say,这个类的作用就是执行具体的动作。
class MyClass
{
public:
//!类的构造函数。
MyClass();
//!这个函数就是类中的“主”函数。
//!这个函数调用Say中的hello函数,
//!然后Say中的hello函数执行具体的动作。(该函数.h中的注释结束)
int run();
private:
//!本类的主要函数返回的值。
int value;
};
//实践发现,这里始终将头文件中注释的简短描述部分作为简短描述。
//如果头文件中只有一句注释,那么也优先将它作为简短描述。
//除非头文件中没有注释,这才将源文件的简短描述作为简短描述。
//后面的详细描述部分先处理.cpp中的函数详细部分注释,再处理.h中的详细部分。
//例如run().
//但是.h中的函数注释如果只有一行,那么就把.h中的函数声明注释当作简短的描述,
//然后再将.cpp的函数实现的注释作为具体的描述。例如MyClass().
#endif
./src/subModule/myclass.cpp内容如下:
#include "myClass.h"
#include "../../lib/say.h"
#include
using std::cout;
using std::endl;
//尽管这样注释,并且头文件中没按照简短格式而只是一句话,
//但这个函数还是以头文件的注释作为简短描述.
//!源文件中的注释
//!构造函数初始化了本类的run函数将要返回的值value。
MyClass::MyClass()
{
value = 1;
}
//(这里尽管在源文件中使用简短注释格式,但是仍旧不是简短注释)
//!源文件中的描述。
//!(.cpp的函数注释)这个函数实际上就是调用Say类的具体执行的函数来进行工作的。
int MyClass::run()
{
cout<<"Begin "<<__FILE__<<":"<<__FUNCTION__<
Say say;
say.hello();
cout<<"the return value is:"<
cout<<"End "<<__FILE__<<":"<<__FUNCTION__<
return value;
}
./lib/say.h内容如下:
#ifndef __SAY_H
#define __SAY_H
//!具体执行动作的类
class Say
{
public:
//!类的构造函数
Say();
//!本类中具体执行的主要起始函数
void hello();
};
#endif
./lib/say.cpp内容如下:
#include "say.h"
#include
using std::cout;
using std::endl;
//!构造函数什么也没有做
Say::Say()
{
}
//!这个函数的功能就是打印一个字符串"hello"。
//!注意开始使用列表功能注释:
//!-# 第1项列表注意
//!-# 第2项列表注意
//! -# 第2.1项列表注意.
//! 2.1的另外一些内容(这里,因没有使用换行命令所以仍旧和上面同一行)
//! -# 第2.2项列表注意
//! 仍旧是2.2的内容
//!哪里?(仍旧是2.2,甚至没有换行)\n
//!这里是2.2的但是用换行了(并且在同一个缩进级别).
//!
//!哪里?(这里结束了所有列表,在文档中紧接这上面的那一行,但是缩进级别已经恢复)
void Say::hello()
{
cout<<"Begin "<<__FILE__<<":"<<__FUNCTION__<
cout<<"hello!"<
cout<<"End "<<__FILE__<<":"<<__FUNCTION__<
}
*问题
===============================
怎样文档化注释不属于任何类的变量和函数。
*注意
===============================
这里,使用的注释风格比较杂乱,主要是为了展示一下doxygen的各种注释风格的用法。实际中最好用同一种注释风格。例如,在C++中写程序,最好就用"//!"开始的风格来进行注释,而C中使用"/**...*/"的等等。