Oracle Solaris Studio 12.2:C++ 用户指南

第 5 章 程序组织

C++ 程序的文件组织需要比典型的 C 程序更加小心。本章说明了如何建立头文件和模板定义。

5.1 头文件

创建有效的头文件是很困难的。头文件通常必须适应 C 和 C++ 的不同版本。要提供模板,请确保头文件能容纳多个包含(幂等)。

5.1.1 可适应语言的头文件

可能需要开发能够包含在 C 和 C++ 程序中的头文件。但是,称为“传统 C”的 Kernighan 和 Ritchie C (K&C)、ANSI C、 Annotated Reference Manual C++ (ARM C++) 以及 ISO C++ 有时要求一个头文件中同一个程序元素有不同的声明或定义。(有关语言和版本之间的变化的其他信息,请参见《C++ 迁移指南》。)要使头文件符合所有这些标准,可能需要根据预处理程序宏 __STDC____cplusplus 的存在情况或值来使用条件编译。

在 K&R C 中没有定义宏 __STDC__,但在 ANSI C 和 C++ 中都对其进行了定义。可使用该宏将 K&R C 代码与 ANSI C 或 C++ 代码区分开。该宏最适用于从非原型函数定义中区分原型函数定义。


#ifdef __STDC__
int function(char*,...);      // C++ & ANSI C declaration
#else
int function();               // K&R C
#endif

在 C 中没有定义宏 __cplusplus,但在 C++ 中对其进行了定义。


注 –

早期版本的 C++ 定义了宏 c_plusplus,但没有定义 cplusplus。现在已不再定义宏 c_plusplus


可使用 __cplusplus 宏的定义来区分 C 和 C++。该宏在保证为函数声明指定 extern "C" 接口时非常有用,如以下示例所示。为了防止出现 extern "C" 指定不一致,切勿将 #include 指令放在 extern "C" 链接指定的作用域中。


#include “header.h”
...                     // ... other include files...
#if defined(__cplusplus)
extern “C” {
#endif
  int g1();
  int g2();
  int g3()
#if defined(__cplusplus)
}
#endif

在 ARM C++ 中,__cplusplus 宏的值为 1。在 ISO C++ 中,该宏的值为 199711L(用 long 常量表示的标准年月)。使用这个宏的值区分 ARM C++ 和 ISO C++。这个宏值在保护模板语法的更改时极为有用。


// template function specialization
#if __cplusplus < 199711L
int power(int,int);                       // ARM C++
#else
template <> int power(int,int);           // ISO C++
#endif

5.1.2 幂等头文件

头文件应当是幂等的。也就是说,多次包括头文件的效果和仅包括一次的效果完全相同。该特性对于模板尤其重要。通过设置预处理程序条件以防止头文件体多次出现,可以很好的实现幂等。


#ifndef HEADER_H
#define HEADER_H
/* contents of header file */
#endif

5.2 模板定义

可以用两种方法组织模板定义:使用包括的定义和使用独立的定义。包括的定义组织允许对模板编译进行更多的控制。

5.2.1 包括的模板定义

在将模板的声明和定义放在使用该模板的文件中时,组织是包括定义的组织。例如:

main.cc


template <class Number> Number twice(Number original);
template <class Number> Number twice(Number original )
    { return original + original; }
int main()
    { return twice<int>(-3); }

使用模板的文件包括了包含模板声明和模板定义的文件时,使用模板的该文件的组织也是包括定义的组织。例如:

twice.h


#ifndef TWICE_H
#define TWICE_H
template <class Number>
Number twice(Number original);
template <class Number> Number twice( Number original )
    { return original + original; }
#endif

main.cc


#include “twice.h”
int main()
    { return twice(-3); }

注 –

使模板标题幂等非常重要。(请参见5.1.2 幂等头文件。)


5.2.2 独立的模板定义

另一种组织模板定义的方法是将定义保留在模板定义文件中,如以下示例所示。

twice.h


#ifndef TWICE_H
#define TWICE_H
template <class Number>
Number twice(Number original);
#endif TWICE_H

twice.cc


template <class Number>
Number twice( Number original )
    { return original + original; }

main.cc


#include “twice.h”
int main( )
    { return twice<int>( -3 ); }

模板定义文件不得包括任何非幂等头文件,而且通常根本不需要包括任何头文件。(请参见5.1.2 幂等头文件。)请注意,并非所有编译器都支持模板的独立定义模型。

一个单独的定义文件作为头文件时,该文件可能会被隐式包括在许多文件中。因此,它不应该包含任何函数或变量定义(除非这些定义是模板定义的一部分)。一个单独的定义文件可以包含类型定义,包括 typedef。


注 –

尽管通常会使用模板定义文件的源文件扩展名(即 .c.C.cc.cpp.cxx.c++),但模板定义文件是头文件。如果需要,编译器会自动包括它们。模板定义文件应单独编译。


如果将模板声明放置在一个文件中,而将模板定义放置在另一个文件中,则必须仔细考虑如何构造定义文件,如何命名定义文件和如何放置定义文件。此外也需要向编译器显式指定定义的位置。有关模板定义搜索规则的信息,请参阅7.5 模板定义搜索

使用 -E-P 选项生成预处理器输出时,定义分离的文件组织不允许在 .i 文件中包含模板定义。编译 .i 文件时会因缺少定义而失败。通过有条件地在模板声明标题中包含模板定义文件(请参见下面的代码示例),可确保通过在命令行中使用 -template=no%extdef 来使用模板定义。libCtd 和 STLport 库以此方式实现。


// templace declaration file
template <class T> class foo { ... };
#ifdef _TEMPLATE_NO_EXTDEF
#include "foo.cc"  //template definition file
#endif

但是,请勿尝试自行定义宏 _TEMPLATE_NO_EXTDEF。如果定义时没有使用 -template=no%extdef 选项,可能会因为包含多个模板定义文件而导致编译失败。