伤城文章网 > IT/计算机 > Sun+Studio+10+C%2B%2B+用户指南

Sun+Studio+10+C%2B%2B+用户指南


C++ 用户指南
Sun? Studio 10

Sun Microsystems, Inc. www.sun.com

文件号码 819-1613-10 2005 年 1 月,修订版 A 请将有关本文档的意见和建议提交至: http://www.sun.com/hwdocs/feedback

版权所有 ? 2005 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, California 95054, U.S.A. 保留所有权利。 美国政府权利-商业软件。政府用户应遵循 Sun Microsystems, Inc. 的标准许可协议,以及 FAR (Federal Acquisition Regulations,即 “联邦 政府采购法规” )的适用条款及其补充条款。 其使用应遵守许可证条款。 本发行可包含第三方开发的材料。 本产品的某些部分可能是从 Berkeley BSD 系统衍生出来的,并获得了加利福尼亚大学的许可。 UNIX 是 X/Open Company, Ltd. 在美国和其他 国家/地区独家许可的注册商标。 Sun、 Sun Microsystems、 Sun 徽标、 Java 和 JavaHelp 是 Sun Microsystems, Inc. 在美国和其他国家/地区的商标或注册商标。所有 SPARC 商标的使用均已获得许可,它们是 SPARC International, Inc. 在美国和其他国家/地区的商标或注册商标。标有 SPARC 商标的产品均基于由 Sun Microsystems, Inc. 开发的体系结构。 本服务手册所介绍的产品以及所包含的信息受美国出口控制法制约,并应遵守其他国家/地区的进出口法律。严禁将本产品直接或间接地用于 核设施、导弹、生化武器或海上核设施,也不能直接或间接地出口给核设施、导弹、生化武器或海上核设施的最终用户。严禁出口或转口到美 国禁运的国家/地区以及美国禁止出口清单中所包含的实体,包括但不限于被禁止的个人以及特别指定的国家/地区的公民。 本文档按 “原样”提供,对于所有明示或默示的条件、陈述和担保,包括对适销性、适用性或非侵权性的默示保证,均不承担任何责任,除非 此免责声明的适用范围在法律上无效。

请回收

目录

开始之前 本书的结构 印刷约定

xxvii xxvii xxviii xxix xxix xxix

Shell 提示符 受支持的平台

访问 Sun Studio 软件和手册页 访问编译器和工具文档 xxxii

访问相关的 Solaris 操作系统文档 访问相关的 C++ 手册页 其他公司出版的书籍 开发人员资源 xxxvi xxxvi xxxvi xxxiv

xxxiv

xxxv

与 Sun 技术支持联系

Sun 欢迎您提出意见和建议 第 I 部分 C++ 编译器 1. C++ 编译参阅器 1.1 1.2 1–1

Sun Studio 10 C++ 5.7 编译器的新特性和新功能 Sun Studio 9 C++ 5.6 编译器的新特性和新功能 1.2.1 1.2.2

1–1 1–3 1–3

对影响常用 SPARC 处理器的缺省设置进行了更改 新的 SPARC 处理器的扩展选项 1–4

iii

1.2.3 1.2.4 1.2.5 1.2.6 1.2.7 1.2.8 1.3 1.4 1.5 1.6 1.7 2.

新的 Intel 处理器的扩展选项 SPARC 和 x86 的新的缺省优化 用于生成更快代码的新选项 用于改进库性能的新选项 用于加速编译的扩展选项 语言增强 1–9 1–9 1–7

1–4 1–5

1–6 1–7 1–7

标准的一致性 C++ 自述文件 手册页 1–10

C++ 公用程序 本地语言支持 2–1

1–10 1–11

使用 C++ 编译器 2.1 2.2 入门 2–1

调用编译器 2.2.1 2.2.2 2.2.3

2–2 2–3 2–3 2–4 2–4

命令语法

文件名称约定 使用多个源文件

2.3 2.4

使用不同编译器版本进行编译 编译和链接 2.4.1 2.4.2 2.4.3 2.4.4 2.4.5 2.4.6 2–5 2–5 2–6 2–6

编译和链接序列 分别编译和链接 一致编译和链接

为 SPARC V9 编译 诊断编译器 2–7

2–7

了解编译器的组织 2–9

2–8

2.5

预处理指令和名称 2.5.1 2.5.2 Pragma

2–9 2–9

具有可变数量参数的宏

iv

C++ 用户指南 ? 2005 年 1 月

2.5.3 2.5.4 2.6

预定义的名称 #error 2–10 2–10

2–10

内存要求 2.6.1 2.6.2 2.6.3 2.6.4

交换空间大小 增加交换空间 虚拟内存的控制 内存要求 2–12 2–12

2–10 2–11 2–11

2.7

简化命令 2.7.1 2.7.2 2.7.3

在 C Shell 中使用别名

2–12 2–13

使用 CCFLAGS 来指定编译选项 使用 make 2–13 3–1

3.

使用 C++ 参阅编译器选项 3.1 3.2 3.3 语法 3–1 3–1

通用指南

按功能汇总的选项 3.3.1 3.3.2 3.3.3 3.3.4 3.3.5 3.3.6 3.3.7 3.3.8 3.3.9 3.3.10 3.3.11 3.3.12 3.3.13

3–2 3–2 3–3

代码生成选项 编译时性能选项 调试选项 浮点选项 语言选项 库选项 3–3 3–4 3–5 3–5

许可证选项 废弃的选项 输出选项

3–6 3–7 3–7 3–8 3–10 3–10

运行时性能选项 预处理程序选项 文件配置选项 参考选项 3–11

目录

v

3.3.14 3.3.15 3.3.16 第 II 部分 编写 C++ 程序 4. 语言扩展参阅 4.1 4.2 4.3 4.4 4.5 4.6 4.7 4.8 4.9 4.10 5.

源文件选项 模板选项 线程选项

3–11 3–11 3–12

4–1 4–1 4–2 4–3 4–3

链接程序作用域 线程局部存储

用限制较少的虚函数覆盖

生成 enum 类型和变量的向前声明 使用不完整 enum 类型 4–4

将 enum 名称作为作用域限定符 使用匿名 struct 声明 传递匿名类实例的地址 4–5 4–6

4–4

将静态名称空间作用域函数声明为类友元 使用函数名称的预定义 __func__ 符号 5–1 5–1 可适应语言的头文件 幂等头文件 5–3 5–3 5–4 5–2 5–1

4–7 4–7

程序组织 5.1

头文件 5.1.1 5.1.2

5.2

模板定义 5.2.1 5.2.2

包括的模板定义 独立的模板定义 6–1 6–1

6.

创建和使用模板 6.1 函数模板 6.1.1 6.1.2

函数模板声明 函数模板定义

6–1 6–2

vi

C++ 用户指南 ? 2005 年 1 月

6.1.3 6.2 类模板 6.2.1 6.2.2 6.2.3 6.2.4 6.3

函数模板用法 6–2 类模板声明 类模板定义

6–2

6–3 6–3 6–4 6–5

类模板成员定义 类模板的用法 6–5

模板实例化 6.3.1 6.3.2

隐式模板实例化 显式模板实例化 6–7 6–8 6–8

6–5 6–6

6.4 6.5 6.6

模板组合

缺省模板参数 模板专门化 6.6.1 6.6.2 6.6.3 6.6.4

模板专门化声明 模板专门化定义

6–8 6–9 6–9

模板专门化使用和实例化 部分专门化 6–10 6–9

6.7

模板问题部分 6.7.1 6.7.2 6.7.3 6.7.4 6.7.5 6.7.6 6.7.7

非本地名称解析和实例化 作为模板参数的本地类型 模板函数的友元声明

6–10 6–11

6–12 6–14

在模板定义内使用限定名称 嵌套模板名称 6–14

引用静态变量和静态函数

6–15 6–15

在同一目录中使用模板生成多个程序 7–1 7–1 7–1 7–1

7.

编译模板 7.1 7.2

冗余编译

系统信息库管理 7.2.1

生成的实例

目录

vii

7.2.2 7.2.3 7.2.4 7.3

整个类实例化 编译时实例化

7–2 7–2 7–2

模板实例的放置和链接 7–3 7–4 7–4 7–5 7–5

外部实例 7.3.1 7.3.2 7.3.3 7.3.4

静态实例 全局实例 显式实例 半显式实例

7.4

模板系统信息库 7.4.1 7.4.2 7.4.3 7.4.4 7.4.5

7–5 7–5 7–6 7–6

系统信息库结构

写入模板系统信息库

从多模板系统信息库读取 共享模板系统信息库 7–6

模板实例自动与 -instances=extern 一致 7–7 7–7 7–7 7–7

7–6

7.5

模板定义搜索 7.5.1 7.5.2 7.5.3

源文件位置约定 定义搜索路径

诊断有问题的搜索 7–8 7–8 7–8 7–9 7–9

7.6

模板选项文件 7.6.1 7.6.2 7.6.3 7.6.4 7.6.5 注释 包括

源文件扩展名 定义源位置

模板专门化条目 8–1 8–1 8–1

7–11

8.

异常处理 8.1 8.2 8.3

同步和异步异常 指定运行时错误 禁用异常 8–2

viii

C++ 用户指南 ? 2005 年 1 月

8.4 8.5 8.6 9.

使用运行时函数和预定义的异常

8–3 8–4

将异常与信号和 Setjmp/Longjmp 混合 生成具有异常的共享库 9–1 9–2 9–2 8–4

类型转换操作 9.1 9.2 9.3 9.4

const_cast

reinterpret_cast static_cast 动态类型转换 9.4.1 9.4.2 9.4.3 9–3 9–4

将分层结构向上类型转换 类型转换到 void* 9–4

9–4

将分层结构向下或交叉类型转换 10–1 10–1 10–2 10–2

9–4

10.

改善程序性能 10.1 10.2 10.3 10.4

避免临时对象 使用内联函数 使用缺省运算符 使用值类 10.4.1 10.4.2 10–3

选择直接传递类

10–3 10–4

在不同的处理器上直接传递类 10–4

10.5 11.

缓存成员变量 11–1

生成多线程程序 11.1

生成多线程程序 11.1.1 11.1.2

11–1 11–2 11–2

表明多线程编译

与线程和信号一起使用 C++ 支持库 11–2

11.2

在多线程程序中使用异常 11.2.1 线程取消 11–2

11.3 11.4

在线程之间共享 C++ 标准库对象 在多线程环境中使用传统 iostream

11–3 11–5
目录 ix

11.4.1 11.4.2 11.4.3 11.4.4 11.4.5 11.4.6 11.4.7 11.4.8 第 III 部分 库 12. 使用库 12.1 12.2 12–1

多线程安全的 iostream 库的组织 对 iostream 库进行接口更改 全局和静态数据 序列执行 对象锁定 11–14 11–15 11–16 11–14

11–6

11–11

多线程安全类 对象析构

11–17 11–18

示例应用程序

C库

12–1 12–1 12–2 12–3

C++ 编译器提供的库 12.2.1 12.2.2 12.2.3 C++ 库描述

访问 C++ 库的手册页 缺省 C++ 库 12–4 12–4

12.3 12.4

相关的库选项 使用类库 12.4.1 12.4.2 12.4.3 12–6

iostream 库 complex 库 链接 C++ 库

12–6 12–7 12–9

12.5 12.6 12.7

静态链接标准库 使用共享库

12–9

12–10 12–11 12–12 12–12

替换 C++ 标准库 12.7.1 12.7.2 12.7.3 12.7.4

可以替换的内容 不可以替换的内容 安装替换库 使用替换库

12–12 12–12

x

C++ 用户指南 ? 2005 年 1 月

12.7.5 13.

标准头文件实现 13–1 13–2 13–3

12–13

使用 C++ 标准库 13.1 13.2 13.3

C++ 标准库头文件 C++ 标准库手册页 STLport 13.3.1 13–13

重新分发和支持的 STLport 库 14–1 14–1 14–2

13–14

14.

使用传统 iostream 库 14.1 14.2 14.3

预定义的 iostream

iostream 交互的基本结构 使用传统 iostream 库 14.3.1 14.3.2 14.3.3 14.3.4 14.3.5 14.3.6 14.3.7 14.3.8 14.3.9 14.3.10

14–3 14–3 14–6

使用 iostream 进行输出 使用 iostream 进行输入 定义自己的提取运算符 使用 char* 提取器 读取任何单一字符 二进制输入 查看输入 提取空白 14–8 14–9 14–9 14–9

14–7

14–7 14–8

处理输入错误

使用具有 stdio 的 iostream 14–11

14–10

14.4

创建 iostream 14.4.1

处理使用类 fstream 的文件 14–14

14–11

14.5 14.6 14.7

iostream 的赋值 格式控制 控制器 14.7.1 14.7.2 14–14 14–15

使用无格式控制器 参数化控制器

14–16

14–17

目录

xi

14.8 14.9

Strstream:数组的 iostream

14–18 14–19

Stdiobuf:stdio 文件的 iostream 14–19 14–19

14.10 Streambuf 14.10.1 14.10.2

和 Streambuf 一起使用 使用 Streambuf 14–20 14–22 14–20

14.11 iostream 手册页 14.12 iostream 术语 15. 使用复数运算库 15.1 复数库 15.1.1 15.2 15–1 15–1 使用复数库

15–2

类型 complex 15.2.1 15.2.2

15–2 15–2

类 complex 的构造函数 算法运算符 15–4 15–5 15–6 15–7 15–3

15.3 15.4 15.5 15.6 15.7 15.8 16.

数学函数 错误处理 输入和输出

混合模式运算 效率 15–7

复数手册页 16–1

15–8

生成库 16.1 16.2 16.3 16.4 16.5 16.6 16.7

了解库

16–1 16–2 16–2 16–3

生成静态 (归档)库 生成动态 (共享)库 生成包含异常的共享库 生成专用的库 生成公用的库 16–4 16–4

生成具有 C API 的库

16–4

xii

C++ 用户指南 ? 2005 年 1 月

16.8 第 IV 部分 附录 A.

使用 dlopen 从 C 程序访问 C++ 库

16–5

C++ 编译器选项 A.1 A.2

A–1 A–2

选项信息的结构 选项参考 A.2.1 A.2.2 A.2.3 A.2.4 A.2.5 A.2.6 A.2.7 A.2.8 A.2.9 A.2.10 A.2.11 A.2.12 A.2.13 A.2.14 A.2.15 A.2.16 A.2.17 A.2.18 A.2.19 A.2.20 A.2.21 A.2.22 A–2

-386 -486 -a

A–2 A–3

A–3 A–3

-Bbinding -c A–4

-cg{89|92}

A–5 A–5

-compat[={4|5}] +d A–7

-D[ ]name[=def] -d{y|n} -dalign -dryrun -E A–11 A–12 A–9 A–10 A–10

A–8

+e{0|1}

-erroff[=t] -errtags[=a] -errwarn[=t] -fast A–15

A–12 A–13 A–14

-features=a[,a...] -filt[=filter[,filter...]] -flags A–24 A–24

A–17 A–21

-fnonstd

目录

xiii

A.2.23 A.2.24 A.2.25 A.2.26 A.2.27 A.2.28 A.2.29 A.2.30 A.2.31 A.2.32 A.2.33 A.2.34 A.2.35 A.2.36 A.2.37 A.2.38 A.2.39 A.2.40 A.2.41 A.2.42 A.2.43 A.2.44 A.2.45 A.2.46 A.2.47 A.2.48 A.2.49 A.2.50

-fns[={yes|no}] -fprecision=p -fround=r A–27

A–24 A–26

-fsimple[=n] -fstore A–29

A–28

-ftrap=t[,t...] -G -g -g0 -H A–31 A–32 A–33 A–33 A–33

A–29

-h[ ]name -help

A–34 A–34

-Ipathname -I-i A–35 A–37

-inline

A–37 A–37 A–38

-instances=a

-instlib=filename -KPIC -KPIC A–39 A–40 A–40

-keeptmp -Lpath -llib

A–40 A–40 A–41

-libmieee -libmil

A–41 A–41

-library=l[,l...] -mc A–45

-migration

A–45

xiv

C++ 用户指南 ? 2005 年 1 月

A.2.51 A.2.52 A.2.53 A.2.54 A.2.55 A.2.56 A.2.57 A.2.58 A.2.59 A.2.60 A.2.61 A.2.62 A.2.63 A.2.64 A.2.65 A.2.66 A.2.67 A.2.68 A.2.69 A.2.70 A.2.71 A.2.72 A.2.73 A.2.74 A.2.75 A.2.76 A.2.77 A.2.78

-misalign -mr[,string] -mt A–46

A–45 A–46

-native -noex

A–47

A–47 A–47

-nofstore -nolib

A–48 A–48 A–48 A–48

-nolibmil -noqueue

-norunpath -O A–48 A–49

-Olevel

-o filename +p -P -p A–49 A–50 A–50

A–49

-pentium -pg -PIC -pic -pta A–51 A–51 A–51 A–51

A–51

-ptipath -pto -ptr -ptv

A–51

A–52 A–52 A–52 A–52

-Qoption phase option[,option... -qoption phase option -qp A–54 A–54

目录

xv

A.2.79 A.2.80 A.2.81 A.2.82 A.2.83 A.2.84 A.2.85 A.2.86 A.2.87 A.2.88 A.2.89 A.2.90 A.2.91 A.2.92 A.2.93 A.2.94 A.2.95 A.2.96 A.2.97 A.2.98 A.2.99 A.2.100 A.2.101 A.2.102 A.2.103 A.2.104 A.2.105 A.2.106

-Qproduce sourcetype -qproduce sourcetype -Rpathname[:pathname... -readme -S -s -sb A–55 A–55 A–56 A–56 A–55

A–54 A–54 A–54

-sbfast

-staticlib=l[,l…]

A–56 A–58

-sync_stdio=[yes|no] -temp=path A–59

-template=opt[,opt... -time -Uname A–61 A–61 A–62

A–59

-unroll=n -V -v A–62 A–62

-vdelx

A–62 A–63

-verbose=v[,v_] +w +w2 -w -Xm -xa A–64 A–64 A–65 A–65 A–65

-xalias_level[=n] -xar A–68 A–69 A–73

A–66

-xarch=isa -xautopar

xvi

C++ 用户指南 ? 2005 年 1 月

A.2.107 A.2.108 A.2.109 A.2.110 A.2.111 A.2.112 A.2.113 A.2.114 A.2.115 A.2.116 A.2.117 A.2.118 A.2.119 A.2.120 A.2.121 A.2.122 A.2.123 A.2.124 A.2.125 A.2.126 A.2.127 A.2.128 A.2.129 A.2.130 A.2.131 A.2.132 A.2.133 A.2.134

-xbuiltin[={%all|%none}] -xcache=c -xcg89 -xcg92 A–75

A–74

A–77 A–77 A–77 A–79

-xchar[=o] -xcheck[=i] -xchip=c -xcode=a

A–79 A–81 A–83 A–84 A–84

-xcrossfile[=n] -xdepend=[yes|no]

-xdumpmacros[=value[,value...]] -xe A–89 A–89 A–90 A–90

-xF[=v[,v...]] -xhelp=flags -xhelp=readme -xia A–91 A–91 A–92

-xildoff -xildon

-xinline[=func_spec[,func_spec...]] -xipo[={0|1|2}] -xjobs=n A–96 A–97 A–94

A–92

-xlang=language[,language] -xldscope={v} -xlibmieee -xlibmil -xlibmopt A–98

A–100

A–100 A–100 A–101

-xlic_lib=sunperf -xlicinfo A–102

目录

xvii

A.2.135 A.2.136 A.2.137 A.2.138 A.2.139 A.2.140 A.2.141 A.2.142 A.2.143 A.2.144 A.2.145 A.2.146 A.2.147 A.2.148 A.2.149 A.2.150 A.2.151 A.2.152 A.2.153 A.2.154 A.2.155 A.2.156 A.2.157 A.2.158 A.2.159 A.2.160 A.2.161 A.2.162

-xlinkopt[=level] -xM -xM1 A–103 A–104 A–104

A–102

-xMerge

-xmaxopt[=v]

A–105 A–105 A–106

-xmemalign=ab

-xnativeconnect[=i] -xnolib A–107 A–109 A–109

-xnolibmil -xnolibmopt -xOlevel

A–110 A–112 A–113 A–114 A–115

-xopenmp[=i] -xpagesize=n

-xpagesize_heap=n -xpagesize_stack=n -xpch=v A–116 A–119

-xpchstop=file -xpg A–119

-xport64[=(v)]

A–120 A–124 A–126

-xprefetch[=a[,a...]]

-xprefetch_auto_type=a -xprefetch_level[=i] -xprofile=p A–127

A–126

-xprofile_ircache[=path] -xprofile_pathmap -xregs=r[,r...] -xrestrict[=f] -xs A–134 A–130 A–132 A–130

A–129

xviii

C++ 用户指南 ? 2005 年 1 月

A.2.163 A.2.164 A.2.165 A.2.166 A.2.167 A.2.168 A.2.169 A.2.170 A.2.171 A.2.172 A.2.173 A.2.174 A.2.175 A.2.176 A.2.177 B. Pragma B.1 B–1

-xsafe=mem -xsb A–134

A–134

-xsbfast -xspace

A–134 A–135 A–135 A–142

-xtarget=t

-xthreadvar[=o] -xtime A–143

-xtrigraphs[={yes|no}] -xunroll=n A–144

A–143

-xustr={ascii_utf16_ushort|no} -xvector[={yes|no}] -xvis[={yes|no}] -xwe A–146 A–147 A–148 A–146

A–145

A–146

-Yc,path -z[ ]arg

Pragma 形式 B.1.1

B–1 B–2

将函数作为 Pragma 参数进行重载 B–2 B–4

B.2

Pragma 引用 B.2.1 B.2.2 B.2.3 B.2.4 B.2.5 B.2.6 B.2.7 B.2.8 B.2.9

#pragma align

#pragma does_not_read_global_data #pragma does_not_return B–5

B–4

#pragma does_not_write_global_data #pragma dumpmacros B–6 B–7

B–5

#pragma end_dumpmacros #pragma fini B–7 B–8

#pragma hdrstop #pragma ident

B–8

目录

xix

B.2.10 B.2.11 B.2.12 B.2.13 B.2.14 B.2.15 B.2.16 B.2.17 术语表 索引

#pragma init

B–8 B–9

#pragma no_side_effect #pragma opt B–9 B–10

#pragma pack(n)

#pragma rarely_called

B–11 B–12 B–12

#pragma returns_new_memory

#pragma unknown_control_flow #pragma weak B–13

术语表 –1 索引 –1

xx

C++ 用户指南 ? 2005 年 1 月



表 P-1 表 P-2 表 2-1 表 2-2 表 3-1 表 3-2 表 3-3 表 3-4 表 3-5 表 3-6 表 3-7 表 3-8 表 3-9 表 3-10 表 3-11 表 3-12 表 3-13 表 3-14 表 3-15 表 3-16

印刷约定 代码约定

xxviii xxviii 3

C++ 编译器识别的文件名称后缀 C++ 编译系统的组件 选项语法格式示例 代码生成选项 编译时性能选项 调试选项 浮点选项 语言选项 库选项 5 6 7 7 8 10 10 3 4 5 2 3 1 8

许可证选项 废弃的选项 输出选项

运行时性能选项 预处理程序选项 文件配置选项 参考选项 源文件选项 模板选项 11 11 11

xxi

表 3-17 表 4-1 表 10-1 表 11-1 表 11-2 表 12-1 表 12-2 表 12-3 表 13-1 表 13-2 表 14-1 表 14-2 表 14-3 表 14-4 表 15-1 表 15-2 表 15-3 表 15-4 表 A-1 表 A-2 表 A-3 表 A-4 表 A-5 表 A-6 表 A-7 表 A-8 表 A-9 表 A-10 表 A-11 表 A-12

线程选项

12 2 4

链接程序作用域声明说明符

在不同架构上结构和联合的传递 iostream 初始核心类 6 多线程安全的可重入公共函数 C++ 编译器附带的库 2 链接 C++ 库的编译器选项 头文件搜索示例 14 2 9 7

C++ 标准库头文件

C++ 标准库手册页 3 iostream 例程头文件 3 iostream 预定义的控制器 15 iostream 手册页概述 21 iostream 术语 复数运算库函数 22 4 4 6

复数数学函数和三角函数

复数运算库函数的缺省错误处理 类型 complex 的手册页 8 选项语法格式示例 选项子节 预定义的宏 -erroff 值 -errwarn 值 -fast 扩展 2 8 13 14 15 1

兼容模式和标准模式的 -features 值 18 仅用于标准模式的 -features 值 19 仅用于兼容模式的 -features 值 20 -filt 值 22

-fns 值 25 -fprecision 值 26

xxii

C++ 用户指南 ? 2005 年 1 月

表 A-13 表 A-14 表 A-15 表 A-16 表 A-17 表 A-18 表 A-19 表 A-20 表 A-21 表 A-22 表 A-23 表 A-24 表 A-25 表 A-26 表 A-27 表 A-28 表 A-29 表 A-30 表 A-31 表 A-32 表 A-33 表 A-34 表 A-35 表 A-36 表 A-37 表 A-38 表 A-39 表 A-40 表 A-41 表 A-42

-fround 值 -fsimple 值 -ftrap 值

27 28

30 38 41 42

-instances 值

用于兼容模式的 -library 值 用于标准模式的 -library 值 -Qoption 值 -Qproduce 值 -staticlib 值 -template 值 -verbose 值 53 54 56 60 63

SPARC 平台的 -xarch 值 69 x86 平台的 -xarch 值 -xcache 的值 76 -xchar 值 78 -xcheck 值 79 -xchip 值 -xcode 值 80 81 72

-xcrossfile 值 83 -xdumpmacros 值 85 -xF 值 90 -xinline 值 -xipo 值 94 99 102 105 92

-xldscope 值 -xlinkopt 值

-xmemalign 的对齐和行为值 -xmemalign 示例 106 107

-xnativeconnect 值 -xopenmp 值 -xport64 值 113 120



xxiii

表 A-43 表 A-44 表 A-45 表 A-46 表 A-47 表 A-48 表 A-49 表 A-50 表 A-51 表 A-52 表 B-1 表 B-2

-xprefetch 值 124 -xprefecth_level 值 -xregs 值 131 127

-xrestrict 值 132 SPARC 平台的 -xtarget 值 -xtarget 的 SPARC 平台名称 Intel 架构上的 -xtarget 扩展 -xthreadvar 的值 142 -xtrigraphs 值 143 -Y 标志 147 10 11 136 136 140

平台上最严格的对齐

存储大小和缺省对齐字节数

xxiv

C++ 用户指南 ? 2005 年 1 月

代码样例

代码样例 6-1 代码样例 6-2 代码样例 7-1 代码样例 7-2 代码样例 7-3 代码样例 7-4 代码样例 7-5 代码样例 7-6 代码样例 7-7 代码样例 7-8 代码样例 7-9 代码样例 7-10 代码样例 11-1 代码样例 11-2 代码样例 11-3 代码样例 11-4 代码样例 11-5 代码样例 11-6 代码样例 11-7 代码样例 11-8

本地类型用作模板参数问题的示例 友元声明问题的示例 冗余定义条目 9 12

11

静态数据成员的定义和简单名称的用法 模板成员函数定义 10 10

10

不同源文件中模板函数的定义 nocheck 选项 special 条目 11 11

何时应使用 special 条目的示例 重载 special 条目 12 专门化模板类 12 13

12

专用化静态模板类成员 检查错误状态 调用 gcount 8 9

用户定义的 I/O 操作 9 禁用多线程安全 10 切换到多线程不安全 10 在多线程不安全的对象中使用同步 11 新增类 11 12

新增类的分层结构

xxv

代码样例 11-9 代码样例 11-10 代码样例 11-11 代码样例 11-12 代码样例 11-13 代码样例 14-1 代码样例 A-1 代码样例 A-2 代码示例 0-1

新增函数

12 15

使用锁定操作的示例

令 I/O 操作和错误检查独立化 16 销毁共享对象 17 18

以多线程安全方式使用 iostream 对象 string 提取运算符 7

预处理程序示例程序 foo.cc 11 使用 -E 选项的 foo.cc 的预处理程序输出 11 带两个指针的循环 133

xxvi

C++ 用户指南 ? 2005 年 1 月

开始之前
本手册指导您如何使用 Sun? Studio 10 的 C++ 编译器, 并提供命令行编译器选项的详细信 息。本手册适用于熟悉 C++ 并对 Solaris? 操作系统和 UNIX? 命令有一定了解的程序员。

本书的结构
本手册包含如下主题: C++ 编译器。第 1 章提供了关于编译的介绍性信息,诸如标准一致性和新特性。第 2 章 说明了如何使用编译器。第 3 章讨论了如何使用编译器的命令行选项。 编写 C++ 程序。第 4 章讨论了如何编译通常可被其他 C++ 编译器接受的非标准代码。 第 5 章建议设置和组织头文件和模板定义。第 6 章讨论了如何创建和使用模板。第 7 章 解释了用于编译模板的各种选项。第 8 章讨论了异常处理,类型转换操作的信息则位于 第 9 章。第 10 章讨论了显著影响 C++ 编译器的性能技术。第 11 章则提供了生成多线 程程序的信息。 库。第 12 章解释了如何使用编译器提供的库。第 13 章讨论了 C++ 标准库。第 14 章讨 论了用于兼容模式的传统 iostream 库。第 15 章讨论了用于兼容模式的复数运算库。第 16 章则提供了关于生成库的信息。

xxvii

印刷约定
表 P-1 字样

印刷约定
含义 示例

AaBbCc123

命令、文件和目录的名称;计算机 屏幕输出

编辑您的 .login 文件。 使用 ls -a 列出所有文件。 % You have mail. % su Password: 请阅读 《用户指南》的第 6 章。 这些称作类选项。 您必须是超级用户才能执行此操作。 要删除文件,请键入 rm filename。

AaBbCc123 AaBbCc123

输入的内容,以便与计算机屏幕输 出相区别 书名、新词或术语以及要强调的词

AaBbCc123
表 P-2 代码符号

命令行变量;用实际名称或值替换

代码约定
含义 表示法 编码示例

[] {} | : ...

括号包含可选参数。 大括号包含所需选项的选项集 合。 分隔变量的 "|" 或 "-" 符号,只 能选择其一。 与逗号一样,分号有时可用于 分隔参数。 省略号表示一系列省略。

O[n] d{y|n} B{dynamic|static} Rdir[:dir] xinline=f1[,...fn]

-O4, -O -dy -Bstatic -R/local/libs:/U/a -xinline=alpha,dos

xxviii

C++ 用户指南 ? 2005 年 1 月

Shell 提示符
Shell 提示符

C shell C shell 超级用户 Bourne shell 和 Korn shell Bourne shell 和 Korn shell 的超级用户

machine-name% machine-name# $ #

受支持的平台
此 Sun Studio 发行版本支持使用如下 SPARC? 和 x86 系列处理器体系结构的系统: UltraSPARC?、 SPARC64、 AMD64、 Pentium 和 Xeon EM64T。可从 http://www.sun.com/bigadmin/hcl 获得硬件兼容性列表,在列表中可以查看您 正在使用的 Solaris 操作系统版本所支持的系统。这些文档给出了平台类型间所有实现 的区别。 在本文档中,术语 “x86”指采用兼容 AMD64 或 Intel Xeon/Pentium 产品系列处理器的 64 位和 32 位系统。有关受支持的系统,请参阅硬件兼容性列表。

访问 Sun Studio 软件和手册页
编译器和工具以及它们的手册页并没有安装到标准的 /usr/bin/ 和 /usr/share/man 目录中。要访问编译器和工具,必须正确设置 PATH 环境变量 (请参阅参阅第 xxx 页 “访问编译器和工具” ) 。要访问手册页,必须正确设置 MANPATH 环境变量 (请参阅 第 xxx 页的 “访问手册页”一节) 。 关于 PATH 变量的更多信息,请参阅 csh(1)、sh(1) 和 ksh(1) 手册页。关于 MANPATH 变 量的更多信息,请参阅 man(1) 手册页。关于设置 PATH 变量和 MANPATH 变量以访问此 发行版本的更多信息,请参阅安装指南或询问系统管理员。

开始之前

xxix

注 – 本节中的信息假设 Sun Studio 编译器和工具安装在 /opt 目录中。如果软件没有安 装在 /opt 目录下,请通过系统管理员获取系统中的等效路径。

访问编译器和工具
使用下列步骤来决定是否需要更改 PATH 变量以访问编译器和工具。

决定是否需要设置 PATH 环境变量
1. 通过在命令提示符后输入下列内容以显示 PATH 变量的当前值。
% echo $PATH

2. 查看输出中是否有包含 /opt/SUNWspro/bin/ 的路径字符串。 如果找到该路径,您的 PATH 变量已经设置好,可以访问编译器和工具了。如果没有找 到该路径,按照下一步中的说明来设置 PATH 环境变量。

设置 PATH 环境变量以访问编译器和工具
1. 如果使用的是 C shell,请编辑起始 .cshrc 文件。如果使用的是 Bourne shell 或 Korn shell,请编辑起始 .profile 文件。 2. 将下列内容添加到 PATH 环境变量。如果已安装 Forte Developer 软件、 Sun ONE Studio 软件或 Sun Studio 软件的其他发行版本,则将以下路径添加到这些安装的路径 之前。 /opt/SUNWspro/bin

访问手册页
使用下列步骤来决定是否需要更改 MANPATH 变量以访问手册页。

xxx

C++ 用户指南 ? 2005 年 1 月

决定是否需要设置 MANPATH 环境变量
1. 通过在命令提示符后输入下列内容以请求 dbx 手册页。
% man dbx

2. 如果有输出的话,请查看输出。 如果 dbx(1) 手册页无法找到或者显示的手册页不是用于安装软件的当前版本,请按照 下一步中的说明来设置 MANPATH 环境变量。

设置 MANPATH 环境变量以访问手册页
1. 如果使用的是 C shell,请编辑起始 .cshrc 文件。如果使用的是 Bourne shell 或 Korn shell,请编辑起始 .profile 文件。 2. 将下列内容增加到 MANPATH 环境变量。
/opt/SUNWspro/man

访问集成开发环境
Sun Studio 集成开发环境 (IDE) 提供了创建、 编辑、 生成、 调试和分析 C、 C++ 或 Fortran 应用程序性能的模块。 启动 IDE 的命令是 sunstudio。有关该命令的详细信息,请参阅 sunstudio(1) 手 册页。 IDE 是否能够正确操作取决于 IDE 能否找到核心平台。 sunstudio 命令会在以下两个 位置查找核心平台:
■ ■

该命令首先在缺省安装目录 /opt/netbeans/3.5V 中查找。 如果该命令在缺省目录未找到核心平台,则它将假设包含 IDE 的目录和包含核心平 台的目录均安装在同一位置上。例如,如果包含 IDE 的目录的路径是 /foo/SUNWspro,则该命令将在 /foo/netbeans/3.5V 中查找核心平台。

如果核心平台未安装在 sunstudio 命令查找它的任一位置上,客户端系统上的每个用 户必须将环境变量 SPRO_NETBEANS_HOME 设置为安装核心平台的位置 (/installation_directory/netbeans/3.5V)。 IDE 的每个用户还必须将 /installation_directory/SUNWspro/bin 添加到其他任何 Forte Developer 软件、 Sun ONE Studio 软件或 Sun Studio 软件发行版本路径前面的 $PATH 中。 路径 /installation_directory/netbeans/3.5V/bin 不可添加到用户的 $PATH 中。

开始之前

xxxi

访问编译器和工具文档
您可以访问下列位置的文档:


可以在随软件一起安装的文档索引 file:/opt/SUNWspro/docs/index.html 中 (位 于本地系统或网络上)获取文档。 如果软件没有安装在 /opt 目录下,请通过系统管理员获取系统中的等效路径。 大多数的手册都可以从 docs.sun.comsm Web 站点上获得。下列书目只能从您所安装 的软件中找到:
■ ■ ■ ■



《标准 C++ 库类参考》 《标准 C++ 库用户指南》 《Tools.h++ 类库参考》 《Tools.h++ 用户指南》

■ ■

发行版本说明可以从 docs.sun.com 网站上获得。 在 IDE 中通过 “帮助”菜单或窗口和对话框上的 “帮助”按钮可以访问 IDE 所有 组件的联机帮助。

您可以通过 Internet 在 docs.sun.com 网站 (http://docs.sun.com) 上阅读、打印和 购买 Sun Microsystems 的各种手册。如果找不到手册,请参阅和软件一起安装在本地系 统或网络中的文档索引。

注 – Sun 对本文档中提到的第三方 Web 站点的可用性不承担任何责任。对于此类站点 或资源中的 (或通过它们获得的)任何内容、广告、产品或其他材料, Sun 并不表示认 可,也不承担任何责任。对于因使用或依靠此类站点或资源中的 (或通过它们获得的) 任何内容、产品或服务而造成的或连带产生的实际或名义损坏或损失, Sun 概不负责, 也不承担任何责任。

xxxii

C++ 用户指南 ? 2005 年 1 月

使用易读格式的文档
该文档以易读格式提供,以方便残障用户使用辅助技术进行阅读。您还可以按照下表所 描述的信息找到文档的易读版本。如果软件没有安装在 /opt 目录下,请通过系统管理 员获取系统中的等效路径。
文档类型 易读版本的格式和位置

手册 (第三方手册除外) 第三方手册: ? 《标准 C++ 库类参考》 ? 《标准 C++ 库用户指南》 ? 《Tools.h++ 类库参考》 ? 《Tools.h++ 用户指南》 自述文件和手册页 联机帮助 发行说明

HTML,位于 http://docs.sun.com HTML,位于安装的软件中的文档索引 file:/opt/SUNWspro/docs/index.html

HTML,位于安装的软件中的文档索引 file:/opt/SUNWspro/docs/index.html 通过 IDE 中的 “帮助”菜单可以使用 HTML HTML,位于 http://docs.sun.com

相关编译器和工具文档
下表描述的相关文档可以在 file:/opt/SUNWspro/docs/index.html 和 http://docs.sun.com 上获得。如果软件没有安装在 /opt 目录中,请通过系统管理员 获取系统中的等效路径。
文档标题 描述

数值计算指南

描述关于浮点计算数值精确性的问题。

开始之前

xxxiii

访问相关的 Solaris 操作系统文档
下表描述了可从 docs.sun.com 网站上获得的相关文档。
文档集合 文档标题 描述

Solaris 参考手册集合 Solaris 软件开发人员集合 Solaris 软件开发人员集合

请参阅手册页部分的标题。

提供有关 Solaris 操作系统的信 息。 描述了 Solaris 操作系统链接编 辑器和运行时链接程序的操作。 涵 盖 POSIX? 和 Solaris 操 作 系 统线程 API、使用同步对象进行 程序设计、编译多线程程序和多 线程程序的查找工具。

链接程序和库指南 多线程编程指南

访问相关的 C++ 手册页
本手册提供了可用于 C++ 库的手册页列表。下表列出了与 C++ 相关的其他手册页。
标题 描述

c++filt dem fbe fpversion gprof ild inline lex rpcgen sigfpe stdarg

按顺序复制每个文件名,并在解码类似 C++ 还原名称的符号之后将文 件名写入标准输出 还原指定的一个或多个 C++ 名称 从汇编语言源文件创建对象文件 打印系统 CPU 和 FPU 的相关信息 生成程序的可执行配置文件 增量链接,允许将修改后的对象代码插入到先前生成的可执行文件 扩展汇编程序的内联过程调用 生成词法分析程序 生成 C/C++ 代码以实现 RPC 协议 允许对指定 SIGFPE 代码进行信号处理 处理变量参数列表

xxxiv

C++ 用户指南 ? 2005 年 1 月

标题

描述

varargs version yacc

处理变量参数列表 显示对象文件或二进制文件的版本标识 将上下文无关的语法转换成一组表,用于执行 LALR(1) 分析算法的简 单自动化

其他公司出版的书籍
以下是部分 C++ 语言书籍的列表。 The C++ Programming Language 3rd edition, Bjarne Stroustrup 所 著 (Addison-Wesley, 1997)。 The C++ Standard Library, Nicolai Josuttis 所著 (Addison-Wesley, 1999)。 Generic Programming and the STL, Matthew Austern 所著 (Addison-Wesley, 1999)。 Standard C++ IOStreams and Locales, Angelika Langer 和 Klaus Kreft 所 著 (AddisonWesley, 2000)。 Thinking in C++, Volume 1, Second Edition, Bruce Eckel 所著 (Prentice Hall, 2000)。 The Annotated C++ Reference Manual, Margaret A. Ellis 和 Bjarne Stroustrup 所 著 (Addison-Wesley, 1990)。 Design Patterns:Elements of Reusable Object-Oriented Software, Erich Gamma、 Richard Helm、 Ralph Johnson 和 John Vlissides 所著 (Addison-Wesley, 1995)。 C++ Primer, Third Edition, Stanley B. Lippman 和 Josee Lajoie 所 著 (Addison-Wesley, 1998)。 Effective C++ - 50 Ways to Improve Your Programs and Designs Second Edition, Scott Meyers 所著 (Addison-Wesley, 1998)。 Scott Meyers 所著 More Effective C++ - 35 Ways to Improve Your Programs and Designs, (Addison-Wesley, 1996)。

开始之前

xxxv

开发人员资源
访问 http://developers.sun.com/prodtech/cc 以查找以下经常更新的资源:
■ ■ ■ ■ ■ ■ ■

关于编程技术和最佳方法的文章 短小编程提示的知识库 编译器和工具组件的文档以及与软件同时安装的文档的更正 支持等级信息 用户论坛 可下载编码示例 新技术预览

您可以在 http://developers.sun.com 上找到对开发人员有用的其他资源。

与 Sun 技术支持联系
如果您有关于本产品的技术问题而本文档未予以解答,请访问: http://www.sun.com/service/contacting

Sun 欢迎您提出意见和建议
Sun 致力于提高文档质量,并欢迎您提出宝贵的意见和建议。请将您的意见发送至以下 URL:
http://www.sun.com/hwdocs/feedback

请在电子邮件的主题行中注明文档的文件号码 (819-1613-10)。 当您提供意见和建议时,可能需要在表单中提供文档英文版本的标题和文件号码。本文 档英文版本的文件号码和标题是:817-0496-10, C++ User's Guide。

xxxvi

C++ 用户指南 ? 2005 年 1 月

第 I 部 分 C++ 编译器

第1章

C++ 编译参阅器
本章提供有关下列内容的信息:
■ ■ ■ ■ ■ ■

第 1-1 页的第 1.1 节 “Sun Studio 10 C++ 5.7 编译器的新特性和新功能” 。 第 1-3 页的第 1.2 节 “Sun Studio 9 C++ 5.6 编译器的新特性和新功能” 。 第 1-9 页的第 1.4 节 “C++ 自述文件” 。 第 1-10 页的第 1.5 节 “手册页” 。 第 1-10 页的第 1.6 节 “C++ 公用程序” 。 第 1-11 页的第 1.7 节 “本地语言支持” 。

1.1

Sun Studio 10 C++ 5.7 编译器的新特性和 新功能
本节简要介绍在 Sun Studio 10 C 5.7 编译器发行版中引入的 C 编译器的新特性和新功 能。有关详细的说明,请参阅每项的交叉引用。




新的 -xarch 选项 -xarch=amd64 指定了 64 位 AMD 指令集编译。有关 -xarch= amd64 的更多信息,请参阅第 A-69 页的第 A.2.105 节 “-xarch=isa” 。 新的 -xtarget 选项 -xtarget=opteron 为 32 位 AMD 编译指定了 -xarch、 -xchip 和 -xcache 设置。有关 -xtarget=opteron 的更多信息,请参阅第 A-135 页的第 A.2.167 节 “-xtarget=t” 。

注 – 要生成 64 位代码, 您必须在命令行中 -fast 和 -xtarget 的右侧指定 -xarch= amd64。例如,指定 CC -fast -xarch=amd64 或 CC -xtarget=opteron -xarch= amd64。新的 -xtarget=opteron 选项并不自动生成 64 位代码。它扩展为 -xarch= sse2、 -xchip=opteron 和 -xcache=64/64/2:1024/64/16,而产生 32 位代码。 -fast 选项也会产生 32 位代码,因为它也是一个定义 -xtarget=native 的宏。
■ ■

除传统 SPARC 平台外,现有 -xarch=generic64 选项现在还支持 x86 平台。 如果指定了 -xarch=amd64, C++ 编译器现在预定义 __amd64 和 __x86_64。

1-1



利用 -xregs 选项 -xregs=[no%]frameptr 仅限 x86 的新标志,您可以将帧指针 寄存器用作未分配的被调用方保存寄存器以提高应用程序的运行时性能。 有关 -xregs=[no%]frameptr 的更多信息,请参阅第 A-130 页的第 A.2.160 节 “ -xregs=r[ ,r...] ” 。



C++ 编译器现在支持模板 - 模板参数。这意味着,可以使用本身就是模板的参数来指 定模板定义,而不是使用类型或值来指定。请回想一下,在类型上实例化的模板本 身就是类型。考虑以下代码示例:
template<typename T> class MyClass { ... }; std::list< MyClass<int> > x;

因为 MyClass<int> 是一种类型,所以代码示例并不使用模板 - 模板参数。然而, 在以下代码示例中,类模板 C 的参数是类模板,而对象 x 则是 C 的实例,它使用类 模板 A 作为其参数。 C 的成员 y 具有类型 A<int>。
// 普通类模板 template<typename T> class A { T x; }; // 具有模板参数的类模板 template< template<typename U> class V > class C { V<int> y; }; // 在模板上实例化 C C<A> x;



在缺省标准模式下, C++ 编译器现在允许嵌套类访问封装类的专有成员。 C++ 标准规定嵌套类对没有封装类成员的特殊访问权限。然而,大多数人认为这种限 制不合理,因为成员函数拥有专有成员的访问权限,因此,成员类也应该有此权限。 在以下示例中,函数 foo 试图访问类 outer 的专有成员。按照 C++ 标准,函数没 有访问权限,除非将其声明为友元函数:
class outer { int i; // 在 outer 中是专有的 class inner { int foo(outer* p) { return p->i; // 无效 } }; };

1-2

C++ 用户指南 ? 2005 年 1 月

C++ 委员会正在采纳对访问规则所做的更改, 以便给成员类授予与成员函数相同的访 问权限。由于预料到将会更改语言规则,很多编译器已经实现了这种规则。 要恢复旧的编译器行为,而禁用这种访问权限,请使用编译器选项 -features= no%nestedaccess。缺省为 -features=nestedaccess。有关 -features 的更 多信息,请参阅第 A-17 页的第 A.2.19 节 “-features=a[,a...]” 。


此发行版本在基于 x86 系统的 Solaris 操作系统和基于 SPARC 系统的 Solaris 操作系 统上提供了 OpenMP API 以实现共享内存并行性。这两种平台现在都启用相同的功 能。

1.2

Sun Studio 9 C++ 5.6 编译器的新特性和新 功能
版本 5.6 的 C++ 编译器引入了以下改进功能和新特性。

1.2.1

对影响常用 SPARC 处理器的缺省设置进行了更改
本发行版本的编译器包括许多与 SPARC 代码生成相关的选项的缺省值的更改。这些更 改反映了针对 UltraSPARC 系列处理器设计新应用程序的当前实践。 对缺省值的更改可能会影响现有的 makefile。特别是,更改将影响为 Ultra 之前的处理器 设计的应用程序。以下各节详细说明了 -xarch、 -xcode、 -xmemalign 和 -xprefetch 选项新的缺省值 :


-xarch 的新缺省值:v8plus

目前, C++ 编译器生成其代码的缺省体系结构是 v8plus (UltraSPARC)。以后的发行 版本将取消对 v7 的支持。 新的缺省设置可以使几乎所有当前使用的计算机的运行时性能更佳。但是,在缺省情 况下,设计用于在 UltraSPARC 之前的计算机上进行部署的应用程序将不会在那些计 算机上执行。使用 -xarch=v8 编译可以确保这些应用程序在那些计算机上执行。 如果要在 v8 系统上部署,则必须在每个编译器命令行以及任何链接时命令上显式指 定选项 -xarch=v8。提供的系统库将可以在 v8 体系结构上运行。 如果要在 v7 系统上部署,则必须在每个编译器命令行以及任何链接时命令上显式指 定选项 -xarch=v7。提供的系统库将使用 v8 指令集。对于本发行版本,唯一支持 v7 的操作系统是 Solaris 8 软件发行版本。遇到 v8 指令时, Solaris 8 操作系统会在软件 中解释指令。程序会运行,但性能将下降。 对于 x86, -xarch 缺省设置为 generic。请注意, x86 上的 -fast 扩展为 -xarch=
native。

有关 -xarch 的详细信息,请参阅第 A-69 页的第 A.2.105 节 “-xarch=isa” 。
第1章 C++ 编译参阅器 1-3



-xcode 的新缺省值

(SPARC) v8 的缺省值是 -xcode=abs32。v9 的缺省值是 -xcode=abs44。有关详细信 息,请参阅第 A-81 页的第 A.2.114 节 “-xcode=a” 。


-xmemalign 的新缺省值

(SPARC) 所有 v8 体系结构的缺省值为 -xmemalign=8i。 所有 v9 体系结构的缺省值为 -xmemalign=8s。有关详细信息,请参阅第 A-105 页的第 A.2.140 节“-xmemalign= ab” 。


-xprefetch 的新缺省值

(SPARC) 目前,缺省值是 -xprefetch=auto,explicit。此更改会对实质上具有非线 性内存访问模式的应用程序造成负面影响。要禁止此更改,请指定 -xprefetch= no%auto,no%explicit。有关详细信息,请参阅第 A-124 页的第 A.2.154 节 “- xprefetch[=a[,a...]]” 。

1.2.2

新的 SPARC 处理器的扩展选项
此发行版本通过新的 -xchip 和 -xtarget 选项提供扩展的 SPARC 支持。 -xchip 和 -xtarget 选 项 目 前 支 持 将 ultra3i 和 ultra4 作 为 值,因 此,您 可 以 生 成 为 UltraSPARC IIIi 和 UltraSPARC IV 处理器进行优化的应用程序。有关详细信息,请参阅 第 A-79 页的第 A.2.113 节“- xchip=c” 和第 A-135 页的第 A.2.167 节“-xtarget=t” 。

1.2.3

新的 Intel 处理器的扩展选项
C++ 编译器支持用于 -xarch、 -xtarget 和 -xchip 的新标志,以及用于 x86 体系结构 的标志。 这些新标志是利用 Pentium 3 和 Pentium 4 芯片与 Solaris 软件对 Intel 平台上的 SSE 和 SSE2 指令的支持结合使用的优势设计的。以下是新标志:
■ ■ ■

-xchip=pentium3 对 Pentium 3 处理器进行优化 -xchip=pentium4 对 Pentium 4 处理器进行优化 -xtarget=pentium3 设置 -xarch=sse、 -xchip=pentium3 和 -xcache= 16/32/4:256/32/4 -xtarget=pentium4 设置 -xarch=sse2、 -xchip=pentium4 和 -xcache= 8/64/4:256/128/8 -xarch=sse 将 SSE 指令集添加到 pentium_pro 指令集体系结构 -xarch=sse2 将 SSE2 指令集添加到 SSE 允许的体系结构



■ ■

警告 – 为了在兼容 SSE/SSE2 Pentium 4 且基于 x86 的 Solaris 操作系统平台上运行而 使用 -xarch={sse|sse2},编译的程序只能在启用 SSE/SSE2 的平台上运行。在不支 持 SSE/SSE2 的平台上运行此类程序会导致发生段故障或错误的结果,并且不会显示任 何显式警告消息。

1-4

C++ 用户指南 ? 2005 年 1 月

以后可能会提供应用于 Solaris 操作系统和编译器的补丁程序,用于禁止在不支持 SSE/SSE2 的平台上执行 SSE/SSE2 编译的二进制文件。 从 Solaris 9 4/04 操作系统开始的 Solaris 操作系统发行版本在兼容 Pentium 4 的平台上 启用 SSE/SSE2。更早版本的 Solaris 操作系统不支持 SSE/SSE2。此警告还适用于使用 利用 SSE/SSE2 指令的 .il 内联汇编语言函数或 __asm( ) 汇编程序代码。 如果单独进行编译和链接,请始终使用编译器和 -xarch={sse|sse2} 进行链接,以确保 链接正确的启动例程。 您可以通过以下准则,确定新的 -xchip、 -xtarget 和 -xarch 标志适用于您需要的组 合:


您是否在运行 Solaris 9 4/04 操作系统更早版本的 Pentium 3 或 Pentium 4 计算机上构 建应用程序,并且指定了 -fast、 -xarch=native 或 -xtarget=native? 如果是这样,编译器将做出以下扩展:


-xarch 设置为 pentium_pro (而不是您希望的 pentium3 或 pentium4) , 因为 Solaris 9 4/04 操作系统以前的操作系统版本不支持 sse 和 sse2 指令。

注 – 尽管使用这些版本的 Solaris 软件,您也可以指定 -xarch=sse 或 -xarch= sse2,但您必须运行从运行 Solaris 9 4/04 或更高版本的计算机上生成的可执行文件, 因为这些版本的软件支持 SSE 和 SSE2 指令。
■ ■

-xchip 相应地设置为 pentium3 或 pentium4。 -xcache 设置为 16/32/4:256/32/4 (对于 Pentium 3 处理器)或 8/64/4:256/128/8

(对于 Pentium 4 处理器) 。


您是否在运行 Solaris 9 4/04 操作系统或更高版本的 Pentium 3 或 Pentium 4 计算机上 构建应用程序,并且指定了 -fast、 -xarch=native 或 -xtarget=native? 如果是这样,编译器将做出以下扩展:
■ ■ ■

-xarch 设置为 sse (对于 Pentium 3)或 sse2 (对于 Pentium 4) 。 -xchip 相应地设置为 pentium3 或 pentium4。 -xcache 设置为 16/32/4:256/32/4 (对于 Pentium 3)或 8/64/4:256/128/8 (对于

Pentium 4) 。 有关详细信息, 请参阅第 A-69 页的第 A.2.105 节 “-xarch=isa” 、 第 A-79 页的第 A.2.113 节 “- xchip=c”和第 A-135 页的第 A.2.167 节 “-xtarget=t” 。

1.2.4

SPARC 和 x86 的新的缺省优化
由于在明确的语言标准和新特性之前编写的软件具有遗留性,传统的优化器已经变得比 较保守,例如 volatile 关键字,现在已经很普通。但是,即使现今的程序已经大大改 进,也应该实施更主动的优化。因此, -O 宏现在扩展为 -xO3 而不是 -xO2 (在 SPARC 和 x86 平台上) 。

第1章

C++ 编译参阅器

1-5

更改缺省设置可以使运行时性能更佳。但是,对于依赖于所有自动被视为 volatile 的变 量的程序,-x03 可能并不适用。做出此假定的典型程序可能包括设备驱动程序,以及实 现自己同步基元的较旧的多线程应用程序。解决方法是使用 -xO2 而不是 -O 进行编译。 有关详细信息,请参阅第 A-15 页的第 A.2.18 节 “-fast” 。

1.2.5

用于生成更快代码的新选项
本发行版本的 C++ 编译器为实现更快的运行时性能提供了众多的增强功能,例如新选 项、改进的循环优化、限定指针的识别,以及对函数级别优化的控制。以下各节详细说 明了这些改进。


新增的 -xprefetch_auto_type 选项 利用 -xprefetch_auto_type 选项,编译器能够使用与生成直接内存访问预取相同 的方式生成由选项 -xprefetch_level=[1|2|3] 指示的循环间接预取。 例如 -xdepend、 -xrestrict 和 -xalias_level 等选项可以增加从 -xprefetch_auto_type 的优化获得的好处。它们可以影响计算候选间接预取的主动 性,进而影响自动间接预取插入的主动性,因为它们有助于生成更好的内存别名歧义 消除信息。有关 -xprefetch_auto_type 选项的详细信息,请参阅第 A-126 页的第 A.2.155 节 “-xprefetch_auto_type=a” 。



循环的优化 C++ 编译器现在支持用于优化循环的以下选项:这些选项需要额外的编译时间,并且 必须与 -xO3 或更高的优化级别一起使用。
■ ■ ■

-xautopar 可以自动并行化循环,适用于多处理器计算机。 -xdepend 可以自动重构循环,适用于所有计算机。 -xvector 可以自动将循环转换为对向量数学库的调用,适用于所有计算机。

有关详细信息,请参阅第 A-73 页的第 A.2.106 节 “- xautopar” 、第 A-84 页的第 A.2.116 节 “- xdepend=[yes|no]”和第 A-146 页的第 A.2.173 节 “- xvector[= {yes|no}]” 。


限定指针的识别 C++ 支持 C 编译器选项 -xrestrict,因此指针限定为由 C99 标准定义的指针。 指定此选项时,编译器假定指针类型的函数参数不引用相同或重叠的对象。从某种意 义来说,此选项对于 C++ 比对于 C 更危险,因为在头文件中定义的内联函数的断言 未必为真。 有关详细信息,请参阅第 A-132 页的第 A.2.161 节 “- xrestrict[=f]” 。 通过 #pragma opt 和 -xmaxopt 控制优化级别 您可以将 #pragma opt 指令与命令行选项 -xmaxopt 结合使用,来指定编译器应用 于单个函数的优化级别。 需要减少特定函数的优化级别时,可以利用这种组合用法,例如,要避免增加代码 (如消除堆栈帧) ,或提高特定函数的优化级别。



1-6

C++ 用户指南 ? 2005 年 1 月

有关详细信息,请参阅第 A-105 页的第 A.2.139 节 “- xmaxopt[=v]” 和第 B-9 页的 第 B.2.12 节 “#pragma opt” 。

1.2.6

用于改进库性能的新选项


新增的 -sync_stdio 选项 导致 I/O 性能较差的一个可能原因是 C++ iostreams 和 C stdio 的同步。 C++ 标准需要 此类同步,因此,编译器在缺省情况下会启用此类同步。但是,您可以通过指定 -sync_stdio=no 禁 用 该 同 步。更 多 信 息 请 参 阅 第 A-58 页 的 第 A.2.88 节 “sync_stdio=[yes|no]” 。

1.2.7

用于加速编译的扩展选项
本发行版本的 C 编译器扩展了预编译头文件工具,使其包含在编译器的某一部分自动生 成预编译头文件的功能。您仍可以选择手动生成预编译头文件,但如果您对编译器的这 种新功能感兴趣,请参阅第 A-116 页的第 A.2.150 节 “- xpch=v” 以了解详细信息。

1.2.8

语言增强
C++ 编译器现在提供对 UTF-16 字符文字和数值转义的扩展支持。此编译器还支持外部 内联函数。


通过 -xustr 扩展的 UTF-16 支持 版本 5.5 的 C++ 编译器引入了对 UTF-16 字符串文字的支持。本发行版本扩展了对 UTF-16 字符文字的支持,该字符文字使用 U’x’ 语法,与用于表达字符串的 U"x" 语 法类似。要识别 UTF-16 字符文字,需要使用相同的 -xustr 选项。例如:
unsigned short ch=U’x’;

本发行版本还支持 UTF-16 字符和字符串文字中的数值转义,此支持与普通字符和字 符串文字中的数值转义类似。例如:
U"ab\123ef" // 字符的八进制表示

U’\xE6’ // 字符的十六进制表示 有关详细信息,请参阅第 A-145 页的第 A.2.172 节 “-xustr= {ascii_utf16_ushort|no}” 。


对外部内联函数的缺省支持

第1章

C++ 编译参阅器

1-7

在 C++ 标准中,与非内联函数类似,内联函数具有外部链接,除非声明为静态。 C++ 5.6 首次为内联函数赋予了外部链接 (在缺省情况下)。如果必须在行外生成内联函 数 (例如,如果需要其地址) ,则仅将一个副本链接到最终程序中。以前,需要副本 的每个对象文件具有自己的带有本地链接的副本。 外部内联函数的这种实现与由更早版本的编译器创建的二进制文件兼容,也就是说, 程序行为符合标准的程度不会比以前低。旧的二进制文件可能具有内联函数的多个本 地副本,而新代码则至多具有外部内联函数的一个副本。 外部内联函数的这种实现与使用本发行版本中包含的 C 5.6 编译器的 C99 版本的内 联函数兼容。也就是说,按照 C 和 C++ 的外部内联函数规则,可以同时在 C 和 C++ 文件中定义相同的内联函数,并且最终程序中将只出现外部函数的一个副本。 编译器之间存在一个不兼容情况,即 C 语言允许非内联函数支持内联函数 (为内联 函数提供外部定义) 。 C++ 语言则没有这个概念。在内联中混合使用两种方法将导致 链接程序错误。假设有这样一个示例:
//File #ifdef extern #endif inline #ifdef } #endif a.h __cplusplus "C" { int f() {return 1;} __cplusplus

//File b.c #include "a.h" int g() {return f();}

在文件 c.c 中,内联函数 f 受某个看起来似乎不支持内联函数的函数的支持。
//File c.c int f() {return 1;}

如果您将 c.c 与 C++ 对 f 的使用混合使用 (如 d.cc 中显示),您将从链接程序中 收到 “multiple definition” (多重定义)错误。
//File d.cc #include "a.h" int h() {return f();}

1-8

C++ 用户指南 ? 2005 年 1 月

解决方法是通知 C 编译器:函数 f 支持内联函数。应该使用以下 c.c 的实现。
//File c.c #include "a.h" extern inline int f();

1.3

标准的一致性
C++ 编译器 (CC) 支持 C++ ISO 国际标准 ISO IS 14882:1998,编程语言 C++。当前发行 版本附带的自述文件描述了与标准需求的所有差异。 编译器提供了对 SPARC V8 和 SPARC V9 (包括 UltraSPARCTM 实现) 在 SPARCTM 平台上, 优化开发功能的支持。在 Prentice-Hall for SPARC International 发行的第 8 版 (ISBN 0-13825001-4) 和第 9 版 (ISBN 0-13-099227-5) SPARC Architecture Manual 中定义了这些功能。 在本文档中, “标准”是指与上面列出的标准版本相一致。 “非标准”或 “扩展”是指 超出这些标准版本的功能。 负责标准的一方可能会不时地修订这些标准。 C++ 编译器兼容的适用标准版本可能被修 订或替换,这将会导致以后的 Sun C++ 编译器发行版本在功能上与旧的发行版本产生不 兼容。

1.4

C++ 自述文件
C++ 编译器的自述文件强调了关于编译器的重要信息,其中包括:
■ ■ ■ ■ ■ ■ ■

在手册印刷之后发现的信息 新特性和更改的特性 软件更正 问题和解决方法 限制和不兼容 可发送库 未实现的标准

要查看 C++ 自述文件的文本格式文件,请在命令提示符后输入以下命令:
example% CC -xhelp=readme

第1章

C++ 编译参阅器

1-9

要访问自述文件的 HTML 格式文件,请在您的 Netscape Communicator 4.0 或兼容版本 的浏览器中打开以下文件:
/opt/SUNWspro/docs/index.html

(如果您的 C++ 编译器软件没有安装在 /opt 目录中,请通过系统管理员获取系统中的 等效路径。 )浏览器可以显示 HTML 文档的索引。要打开自述文件, 请在索引中查找它 的对应条目,然后单击主题。

1.5

手册页
联机手册 (man) 页提供了关于命令、函数、例行程序以及收集这些信息的文档。 可以通过运行以下命令来显示手册页:
example% man topic

在整个 C++ 文档中,手册页参考以主题名称和手册节编号显示:cc(1) 通过 man cc 进 行访问。其他部分 (例如用 ieee_flags(3M) 表示的节)要使用 man 命令和该命令的 -s 选项来访问:
example% man -s 3M ieee_flags

1.6

C++ 公用程序
以下 C++ 公用程序现已并入传统的 UNIX? 工具并且与 UNIX 操作系统捆绑在了一起:
■ ■ ■ ■ ■

lex 生成文本简单词法分析的程序 yacc 根据语法生成 C 函数分析输入流 prof 生成程序模块的执行配置文件 gprof 按过程来配置程序运行时性能 tcov 按语句来配置程序运行时性能

关于这些 UNIX 工具的更多信息,请参阅 《程序性能分析工具》和相关的手册页。

1-10

C++ 用户指南 ? 2005 年 1 月

1.7

本地语言支持
此发行版本的 C++ 支持使用英语以外的其他语言进行应用程序的开发, 包括了大多数欧 洲语言和日语。因此,您可以十分便捷地将应用程序从一种语言切换到另一种语言。此 功能被称为国际化。 通常, C++ 编译器按如下方式实现国际化:
■ ■ ■ ■

C++ 从国际化的键盘识别 ASCII 字符(也就是说,它具有键盘独立性和 8 位清除) 。 C++ 允许使用本地语言打印某些消息。 C++ 允许在注释、字符串和数据中使用本地语言。 C++ 只支持符合扩展 UNIX 字符 (EUC) 的字符集,在该字符集中字符串内的每个空 字节是一个空字符,而每个 "/" 的 ascii 值是 "/" 字符。

变量名称不能国际化,必须使用英文字符集。 您可以设置语言环境将应用程序从一种本地语言更改为另一种语言。关于这一点和其他 本地语言支持功能的信息,请参阅操作系统文档。

第1章

C++ 编译参阅器

1-11

1-12

C++ 用户指南 ? 2005 年 1 月

第2章

使用 C++ 编译器
本章描述了如何使用 C++ 编译器。 任何编译器的主要用途是将高级语言 (如 C 参阅 ++)编写的程序转换成目标计算机硬 件可执行的数据文件。您可以使用 C++ 编译器完成以下任务:


将源文件转换成可重定位的二进制 (.o) 文件、静态 (归档)库 (.a) 文件 (使用 -xar) 或动态 (共享)库 (.so) 文件。其中二进制文件可以在以后链接成可执行文件。 将对象文件或库文件 (或两者)链接或重链接成可执行文件 启用运行时调试 (-g) 来编译可执行文件 启用运行时语句或过程级别的文件配置 (-pg) 来编译可执行文件

■ ■ ■

2.1

入门
本节简要概述了如何使用 C++ 编译器编译和运行 C++ 程序。关于命令行选项的完整参 考,请参阅附录 A。

注 – 本节中的命令行示例显示了 CC 的用法。打印输出可能会稍有不同。
生成和运行 C++ 程序的基本步骤包括: 1. 使用编辑器创建具有表 2-1 中列出的有效后缀之一的 C++ 源文件 2. 调用编译器来生成可执行文件 3. 通过输入可执行文件的名称来启动程序

2-1

以下程序在屏幕上显示消息:
example% cat greetings.cc #include <iostream> int main() { std::cout << "Real programmers write C++!" << std::endl; return 0; } example% CC greetings.cc example% a.out Real programmers write C++! example%

在此示例中, CC 编译源文件 greetings.cc,并且在缺省情况下将编译可执行程序生成 a.out 文件。要启动程序,请在命令行提示符后输入可执行文件 a.out 的名称。 按传统方法, UNIX 编译器为可执行文件 a.out 命名。每次编译都写入到同一个文件是 比较笨拙的方法。另外,如果已经有这样一个文件存在,下次运行编译器时该文件将被 覆盖。如以下示例所示,改为使用 -o 编译器选项来指定可执行输出文件的名称:
example% CC -o greetings greetings.C

(由单独源文件组 在此示例中, -o 选项告知编译器将可执行代码写入文件 greetings。 成的程序通常提供无后缀的源文件名称。 ) 或者,可以在每次编译后使用 mv 命令来重命名缺省的 a.out 文件。无论是哪种方式, 都可以输入可执行文件的名称来运行程序:
example% greetings Real programmers write C++! example%

2.2

调用编译器
本节的其他部分讨论了使用 CC 命令的约定、编译器源代码行指令和编译器使用的其他 有关问题。

2-2

C++ 用户指南 ? 2005 年 1 月

2.2.1

命令语法
编译器命令行的通用语法如下所示:
CC [options] [source-files] [object-files] [libraries]
option 是前缀为短线 (-) 或加法符号 (+) 的选项关键字。某些选项带有参数。

通常,编译器选项的处理是由左到右,允许有选择地覆盖宏选项 (即包括其他选项的选 项) 。在大多数的情况下,如果您多次指定同一个选项,那么最右边的赋值会覆盖前面 的赋值,而不会累积。注意以下特殊情况:


所有的链接程序选项和 -features、 -I、 - l、 -L、 - library、 -pti、 -R/-staticlib、 -U、 - verbose、 - xdumpmacros 和 -xprefetch 选项将会累积而不会覆盖。 所有的 -U 选项都在所有的 -D 选项后处理。



源文件、对象文件和库按它们在命令行上出现的顺序编译并链接。 在以下示例中, CC 用于编译两个源文件 (growth.C 和 fft.C)以在启用运行时调试的 情况下生成名为 growth 的可执行文件。
example% CC -g -o growth growth.C fft.C

2.2.2

文件名称约定
出现在命令行上文件名称的附加后缀决定了编译器处理文件的方式。如果文件名称的后 缀没有在下表中列出,或文件名称没有后缀,那么都要传递到链接程序。
表 2-1 后缀

C++ 编译器识别的文件名称后缀
语言 操作

.c .C .cc .cpp .cxx .c++ .i

C++ C++ C++ C++ C++ C++ C++

作为 C++ 源文件编译,将对象文件放入当前目录;对象文件的 缺省名称是带有 .o 后缀的源名称。 与 .c 后缀相同的操作。 与 .c 后缀相同的操作。 与 .c 后缀相同的操作。 与 .c 后缀相同的操作。 与 .c 后缀相同的操作。 将预处理程序输出文件作为 C++ 源文件处理。与 .c 后缀相同 的操作。

第2章

使用 C++ 编译器

2-3

表 2-1 后缀

C++ 编译器识别的文件名称后缀 ( 续 )
语言 操作

.s .S .il

汇编程序 汇编程序 内联扩展

使用汇编程序的汇编源文件。 使用 C 语言预处理程序和汇编程序的汇编源文件。 处理内联扩展的汇编内联模板文件。编译器将使用模板来扩展 选定例程的内联调用。 (内联模板文件是特殊的汇编文件。请参 阅 inline(1) 手册页。 ) 将对象文件传递到链接程序。

.o .a .so .so.n

对象文件

静态(归档) 将对象库名传递到链接程序。 库 动态(共享) 将共享对象的名称传递到链接程序。 库

2.2.3

使用多个源文件
C++ 编译器在命令行上接受多个源文件。编译器编译的单独源文件与编译器直接或间接 支持的任何文件一起称为编译单元。 C++ 将每个源作为一个单独的编译单元处理。

2.3

使用不同编译器版本进行编译
从 C++ 5.1 编译器开始,编译器就使用标识模板缓存版本的字符串来标记模板缓存目 录。 此编译器在缺省情况下不使用缓存。只有指定 -instances=extern 后,该编译器才使 用缓存。如果编译器使用缓存,就要检查缓存目录的版本,并在遇到缓存版本问题时发 出错误消息。以后的 C++ 编译器也会检查缓存的版本。例如,具有不同模板缓存版本标 识的未来版本编译器在处理此发行版本的编译器生成的缓存目录时,会发出与以下消息 类似的错误:
./SunWS_cache 的模板数据库与此编译器不兼容

编译器遇到新版本的编译器生成的缓存目录时,也会发出类似的错误。 尽管 C++ 5.0 编译器生成的模板缓存目录没有标记版本标识符,但是当前编译器在处理 5.0 缓存目录时不会发出错误或警告。 编译器将 5.0 缓存目录转换为编译器使用的目录格 式。

2-4

C++ 用户指南 ? 2005 年 1 月

C++ 5.0 编译器不能使用新版本编译器生成的缓存目录。 C++ 5.0 编译器不能识别格式差 异,并在遇到由 C++ 5.1 或新版本编译器生成的缓存目录时将会发出断言。 升级编译器时,最好清除缓存。在包含模板缓存目录 (在大多数的情况下,模板缓存 目录命名为 SunWS_cache) 的每个目录上运行 CCadmin -clean。或者,可以使用 rm rf SunWS_cache。关于如何清除模板的最新说明,请参阅 http://forte.sun.com/s1scc/articles/index.html 上的技术文章 “升级 C++ 编译器” 。

2.4

编译和链接
本节描述了编译和链接程序的某些方面。在以下示例中,CC 用来编译三个源文件,并链 接对象文件以生成名为 prgrm 的可执行文件。
example% CC file1.cc file2.cc file3.cc -o prgrm

2.4.1

编译和链接序列
在上述示例中,编译器自动生成加载器对象文件(file1.o、file2.o 和 file3.o) ,然 后调用系统链接程序为文件 prgrm 创建可执行程序。 编译以后,仍然保留对象文件 (file1.o、 file2.o 和 file3.o)。此约定让您易于重 新链接和重新编译文件。

注 – 如果在同一个操作中仅编译一个源文件和链接一个程序,则相应的 .o 文件被自动 删除。要保留所有的 .o 文件,除非要编译多个源文件,否则请不要在同一操作中进行 编译和链接。
如果编译失败,您将收到每个错误的对应消息。不会为那些有错的源文件生成 .o 文件, 也不会为它们生成可执行程序。

第2章

使用 C++ 编译器

2-5

2.4.2

分别编译和链接
您可以分别进行编译和链接。 -c 选项编译源文件并生成 .o 对象文件,但不创建可执行 文件。没有 -c 选项,编译器将调用链接程序。通过将编译和链接步骤分开,仅修复一 个文件而不需要完全重新编译。以下示例显示了如何以独立的步骤编译一个文件并与其 他文件链接:
example% CC -c file1.cc example% CC -o prgrm file1.o file2.o file3.o

生成新的对象文件 生成可执行文件

确保链接步骤列出了生成完整程序所需的全部对象文件。如果这一步骤中丢失了所有的 对象文件,则链接将失败并出现 “未定义的外部引用”错误 (丢失例程) 。

2.4.3

一致编译和链接
如果以独立的步骤编译和链接,那么使用以下编译器选项来进行一致的编译和链接是十 分重要的: ■ -B ■ -compat ■ -fast ■ -g ■ -g0 ■ -library ■ -misalign ■ -mt ■ -p ■ -xa ■ -xarch ■ -xcg92 and -xcg89 ■ -xipo ■ -xpagesize ■ -xpg ■ -xprofile ■ -xtarget 如果您使用这些选项之一来编译子程序,请确保使用相同的选项进行链接:


在使用 -library、-fast、 - xtarget 和 -xarch 选项时, 必须确保包括了链接程序选 项。如果编译和链接同时进行的话,就可以忽略这些链接程序选项。 对于 -p、 -xpg 和 -xprofile,将选项包括在一个阶段而从其他阶段排除并不影响程 序的正确性,但是您将不能进行文件配置。



2-6

C++ 用户指南 ? 2005 年 1 月



对于 -g 和 -g0,将选项包括在一个阶段而从其他状态排除不影响程序的正确性,但 影响调试程序的能力。任何使用 -g 或 -g0 来链接程序,但没有使用这两个选择中的 一个进行编译的模块将无法正确调试。注意,调试通常需要使用 -g 选项或 -g0 选项 编译具有函数 main 的模块。

以下示例中,使用 -xcg92 编译器选项来编译程序。此选项是用于 -xtarget=ss1000 的 宏,可以扩展为: - xarch=v8 -xchip=super -xcache=16/64/4:1024/64/1。
example% CC -c -xcg92 sbr.cc example% CC -c -xcg92 smain.cc example% CC -xcg92 sbr.o smain.o

如果程序使用模板,则某些模板可能会在链接期间被实例化。在这种情况下,来自最后 一行 (链接行)的命令行选项将用于编译实例化的模板。

2.4.4

为 SPARC V9 编译
只有在运行 64 位内核的 V9 SPARC Solaris 8 操作系统中才支持 64 位对象的编译、 链接 和执行。 64 位编译由 -xarch=v9、 -xarch==v9a 和 -xarch=v9b 选项来指示。

2.4.5

诊断编译器
可以使用 -verbose 选项在编译程序的同时显示帮助信息,例如编译器调用的程序名称 和版本号以及每个编译阶段的命令行。 编译器无法识别的命令行上的任何参数被解释为链接程序选项、对象程序文件名或库 名。 基本区别是:
■ ■

无法识别的选项会生成警告。这些选项一般带有前缀短线 (-) 或加法符号 (+)。 无法识别的非选项不会生成警告。这些非选项一般不带有前缀短线或加法符号。 (然 而,这些选项会传递到链接程序。如果链接程序无法识别它们,将会生成链接程序错 误消息。 )

以下示例中,注意 CC 无法识别 -bit,该选项被传递到尝试解释它的链接程序 (ld)。因 为单字母 ld 选项可以被连在一起,所以链接程序将 -bit 视为 -b -i -t,所有这些都 是合法的 ld 选项。这可能并不是您所希望看到的结果:
example% CC -bit move.cc <- -bit 不是一个可识别的 CC 选项

CC:警告:如果调用了 ld,则将选项 -bit 传递至 ld,否则会将其忽略

第2章

使用 C++ 编译器

2-7

以下示例中,用户打算输入 CC 选项 -fast,但是省略前导短线。编译器又一次将参数 传递到链接程序,而链接程序将参数解释为文件名称:
example% CC fast move.cc <- 用户要输入 -fast。 move.CC: ld: 致命的:文件 fast:打开失败; errno=2 ld: 致命的:文件处理失败。 No output written to a.out

2.4.6

了解编译器的组织
C++ 编译器软件包由前端、优化器、代码生成器、汇编程序、模板预链接程序和链接编 辑器组成。除非您使用命令行选项进行指定,否则 CC 命令将逐个自动调用这些组件。 因为这些组件中的任何一个都可能生成错误,并且各个组件执行不同的任务,所以标识 生成错误的组件是有意义的。使用 -v 和 -dryrun 选项来帮助实现这一目的。 正如下表所示,不同编译器组件的输入文件拥有不同的文件名后缀。后缀建立了要进行 的编译类型。关于文件后缀的含义请参阅表 2-1。
表 2-2 组件

C++ 编译系统的组件
描述 使用说明

ccfe iropt ir2hf inline ube_ipa fbe cg ube CClink ld ild

前端 (编译器预处理程序和编译器) SPARC:代码优化器 x86:中间语言转换器 SPARC:汇编语言模板的内联扩展 x86:程间的分析器 汇编程序 SPARC:代码生成器、内联函数、汇编程序 x86:代码生成器 模板预链接程序 非递增式链接编辑器 递增式链接编辑器 -g, -xildon -xO[2-5], -fast -xO[2-5], -fast -xO[2-5], -fast 指定的 .il 文件 -xcrossfile=1 和 -xO4、 - xO5 或 -fast

2-8

C++ 用户指南 ? 2005 年 1 月

2.5
2.5.1

预处理指令和名称
本节讨论了关于预处理 C++ 编译器所特有的指令信息。

Pragma
预处理程序关键字 pragma 是 C++ 标准的一部分,但 pragma 的形式、内容和含义对每 个编译器是不同的。关于 C++ 编译器识别的 pragmas 列表,请参阅附录 B。

2.5.2

具有可变数量参数的宏
C++ 编译器接受以下形式的 #define 预处理程序指令。
#define identifier (...) replacement_list #define identifier (identifier_list, ...) replacement_list

如果列出的宏参数以省略号结尾,那么该宏的调用允许使用除了宏参数以外的其他更多 参数。附加参数被收集在一个单独的字符串中,该字符串可以包括逗号。可以使用宏替 以下示例说明了如何使用可变参数 换列表中的名称 __VA_ARGS__ 来引用这些附加参数。 列表的宏。
#define debug(...) fprintf(stderr, __VA_ARGS__) #define showlist(...) puts(#__VA_ARGS__) #define report(test, ...)((test)?puts(#test):\ printf(__VA_ARGS__)) debug(“Flag”); debug(“X = %d\n”,x); showlist(The first, second, and third items.); report(x>y, "x is %d but y is %d", x, y);

其结果如下:
fprintf(stderr, "Flag"); fprintf(stderr, “X = %d\n”, x); puts(“The first, second, and third items.”); ((x>y)?puts("x>y"):printf("x is %d but y is %d", x, y));

第2章

使用 C++ 编译器

2-9

2.5.3

预定义的名称
附录中的表 A-3 显示了预定义的宏。您可以在 #ifdef 这样的预处理程序条件中使用这 些值。 +p 选项防止了 sun、 unix、 sparc 和 i386 预定义宏的自动定义。

2.5.4

#error
在发出警告以后,#error 指令不会继续编译。指令原来的行为是发出警告并继续编译。 其新行为 (和其他编译器保持一致)是发出错误消息并立即停止编译。编译器退出并报 告失败。

2.6

内存要求
编译需要的内存量取决于多个参数,包括:
■ ■ ■ ■

每个过程的大小 优化级别 为虚拟内存设置的限制 磁盘交换文件的大小

在 SPARC 平台上,如果优化器用完了所有内存,那么它将通过在较低优化级别上重试 当前过程来尝试恢复。然后优化器将在命令行用 -xOlevel 选项指定的原优化级别上,继 续随后的例程。 如果编译包括大量例程的单独源文件,编译器可能会用完所有内存或交换空间。如果编 译器用完了内存,可以尝试降低优化级别。或者,可以将多例程的源文件分割为单例程 的文件。

2.6.1

交换空间大小
swap -s 命令显示了可用的交换空间。更多信息请参阅 swap(1M) 手册页。

以下示例显示了 swap 命令的使用:
example% swap -s total:40236k bytes allocated + 7280k reserved = 47516k used, 1058708k available

2-10

C++ 用户指南 ? 2005 年 1 月

2.6.2

增加交换空间
使用 mkfile(1M) 和 swap (1M) 来增加工作站上交换空间的大小。 (您必须成为超级用 户才能执行该操作。 )mkfile 命令创建指定大小的文件,而 swap -a 将文件增加到系统 交换空间:
example# mkfile -v 90m /home/swapfile /home/swapfile 94317840 bytes example# /usr/sbin/swap -a /home/swapfile

2.6.3

虚拟内存的控制
在 -xO3 或更高级别上编译大型例程 (单个过程中包含了几千行代码)会需要大量的内 存。在这种情况下,系统性能可能降低。您可以通过限制单个进程的可用虚拟内存量来 控制这种情况。 要限制 sh shell 的虚拟内存,请使用 ulimit 命令。更多信息请参阅 sh(1) 手册页。 以下示例显示了如何将虚拟内存限制为 16M。
example$ ulimit -d 16000

在 csh shell 中,使用 limit 命令来限制虚拟内存。更多信息请参阅 csh(1) 手册页。 下一个示例也显示了如何将虚拟内存限制为 16M。
example% limit datasize 16M

这些示例都使优化器在数据空间达到 16M 时尝试恢复。 虚拟空间的限制不能大于系统总的可用交换空间。在实际使用时,虚拟空间的限制要足 够的小,以允许在大型编译过程中正常使用系统。 请确保编译不会消耗一半以上的交换空间。 对于 32M 的交换空间,请使用以下命令: 在 sh shell 中:
example$ ulimit -d 16000

第2章

使用 C++ 编译器

2-11

在 csh shell 中:
example% limit datasize 16M

最佳设置取决于要求的优化程度、实际内存量和可用的虚拟内存量。

2.6.4

内存要求
工作站至少需要 64M 的内存,推荐使用 128M。 要决定实际内存,请使用以下命令:
example% /usr/sbin/dmesg | grep mem mem = 655360K (0x28000000) avail mem = 602476544

2.7

简化命令
您可以通过使用 CCFLAGS 环境变量或通过使用 make 来定义特殊的 shell 别名, 简化复杂 的编译器命令。

2.7.1

在 C Shell 中使用别名
以下示例为带有常用选项的命令定义了别名。
example% alias CCfx "CC -fast -xnolibmil"

下面的示例使用了别名 CCfx。
example% CCfx any.C

命令 CCfx 现在等价于:
example% CC -fast -xnolibmil any.C

2-12

C++ 用户指南 ? 2005 年 1 月

2.7.2

使用 CCFLAGS 来指定编译选项
您可以设置 CCFLAGS 变量来指定选项。
CCFLAGS 变量可以在命令行中显式使用。下列示例说明了如何设置 CCFLAGS (C Shell):

example% setenv CCFLAGS '-xO2 -xsb'

下面的示例显式使用 CCFLAGS。
example% CC $CCFLAGS any.cc

当您使用 make 时,如果 CCFLAGS 变量像上述示例那样设置,并且 makefile 的编译规则 是隐式的,那么调用 make 会导致编译等价于:
CC -xO2 -xsb files...

2.7.3

使用 make
make 公用程序是功能十分强大的程序开发工具,可以方便地和所有 Sun 编译器一起使 用。更多信息请参阅 make(1S) 手册页。

2.7.3.1

和 make 一起使用 CCFLAGS
使用 makefile 的隐式编译规则时 (即没有 C++ 编译行) , make 程序自动使用 CCFLAGS。

2.7.3.2

为 Makefile 增加后缀
您可以将不同的文件后缀增加到 makefile 以使它们收入 C++ 中。以下示例将 .cpp 增加 为 C++ 文件的有效后缀。将 SUFFIXES 宏增加到 makefile:
SUFFIXES:.cpp .cpp~

(此行可以放置在 makefile 的任何位置。 )

第2章

使用 C++ 编译器

2-13

将以下各行增加到 makefile。缩进的行必须以制表符开头。
.cpp: $(LINK.cc) -o $@ $< $(LDLIBS) .cpp~: $(GET) $(GFLAGS) -p $< > $*.cpp $(LINK.cc) -o $@ $*.cpp $(LDLIBS) .cpp.o: $(COMPILE.cc) $(OUTPUT_OPTION) $< .cpp~.o: $(GET) $(GFLAGS) -p $< > $*.cpp $(COMPILE.cc) $(OUTPUT_OPTION) $< .cpp.a: $(COMPILE.cc) -o $% $< $(COMPILE.cc) -xar $@ $% $(RM) $% .cpp~.a: $(GET) $(GFLAGS) -p $< > $*.cpp $(COMPILE.cc) -o $% $< $(COMPILE.cc) -xar $@ $%

2.7.3.3

和标准库头文件一起使用 make
标准库文件的名称不包括 .h 后缀。相反,它们命名为 istream、fstream 等等。此外, 模板源文件命名为 istream.cc、 fstream.cc 等等。

2-14

C++ 用户指南 ? 2005 年 1 月

第3章

使用 C++ 参阅编译器选项
本章说明了如何使用命令行 C++ 编译器选项,并按功能汇总它们的使用。关于选项的 详细解释,请参阅附录 A。

3.1

语法
下表显示了本书中使用的典型选项语法格式的示例。
表 3-1 语法格式

选项语法格式示例
示例

-option -optionvalue -option=value -option value

-E -Ipathname -xunroll=4 -o filename

圆括号、大括号、括号、管道字符和省略号是选项说明中使用的元字符,而不是选项自 身的一部分。关于使用语法的详细解释,请参阅本手册前面的 “开始之前”中的印刷 约定。

3.2

通用指南
C++ 编译器选项的某些通用指南:


-llib 选项和 liblib.a 库 (或 liblib.so) 一起链接。将 -llib 放置在源文件和对象 文件后面可以确保搜索库时的顺序更加安全。
3-1



一般,编译器选项的处理是由左到右 (除了 -U 选项要在所有 -D 选项处理以后) , 允许有选择性地覆盖宏选项 (即包括其他选项的选项) 。此规则不适用于链接程序 选项。
features、 -I、 -l、 -L、 -library、 -pti、 -R、 -staticlib、 -U、 -verbose 和 -xprefetch 选项将会累积而不会覆盖。 D 选项会积累,不过同一个名称的多个 -D 选项会互相覆盖。





源文件、对象文件和库按它们在命令行上出现的顺序编译和链接。

3.3

按功能汇总的选项
在本节中,编译器选项按功能分组以便提供快速参考。关于每个选项的详细描述,请参 阅附录 A。 选项适用于除了说明以外的所有平台;基于 SPARC 系统的 Solaris 操作系统专有的特性 标识为 SPARC,而基于 x86 系统的 Solaris 操作系统专有的特性标识为 x86。

3.3.1

代码生成选项
以下代码生成选项按字母顺序列出。
表 3-2 选项

代码生成选项
操作

-compat +e{0|1} -g -KPIC -KPIC -mt -xcode=a -xMerge +w

设置编译器的主发行版本兼容模式。 控制虚拟表的生成。 用于与调试一起使用的编译。 生成位置独立的代码。 生成位置独立的代码。 编译和链接多线程代码。 (SPARC) 指定代码地址空间。 (SPARC) 将数据段和文本段合并。 标识会产生不可预料结果的代码。

3-2

C++ 用户指南 ? 2005 年 1 月

表 3-2 选项

代码生成选项 ( 续 )
操作

+w2 -xregs

发出由 +w 提供的所有警告,以及关于技术违规的附加警告。这 类技术违规可能是无害的,但可能会降低系统的最大可移植性。 如果编译器可以使用更多的寄存器用于临时存储 (临时寄存 器) ,那么编译器将能生成速度更快的代码。该选项使得附加临 时寄存器可用,而这些附加寄存器通常是不适用的。 链接程序选项。

-z arg

3.3.2

编译时性能选项
以下编译时性能选项按字母顺序列出
表 3-3 选项

编译时性能选项
操作

-instlib -xjobs -xpch -xpchstop -xprofile_ircache -xprofile_pathmap

禁止生成已出现在指定库中的模板实例。 设置编译器可以为完成工作而创建的进程数量。 可以减少应用程序的编译时间,该应用程序的源文件共享共同的包 括文件集合。 指定最后一个包含文件,用于考虑用 -xpch 创建预编译头文件。 (SPARC) 重用 -xprofile=collect 期间保存的编译数据。 (SPARC) 支持单个配置文件目录中的多个程序或共享库。

3.3.3

调试选项
以下调试选项按字母顺序列出。
表 3-4 选项

调试选项
操作

+d -dryrun -E -g -g0

不扩展 C++ 内联函数。 显示但不编译由驱动程序传递到编译器的选项。 仅在 C++ 源文件上运行预处理程序,并将结果发送至 stdout。 用于与调试一起使用的编译。 为了调试而编译,但不禁止内联。

第3章

使用 C++ 参阅编译器选项

3-3

表 3-4 选项

调试选项 ( 续 )
操作

-H -keeptmp -migration -P -Qoption -readme -s -temp=dir -verbose=vlst -xcheck -xdumpmacros -xe -xhelp=flags -xildoff -xildon -xport64 -xs -xsb -xsbfast

打印包含文件的路径名称。 保留编译时创建的临时文件。 解释可以从早期编译器获得有关移植信息的位置。 仅预处理源文件,输出到 .i 文件。 直接将选项传递给编译阶段。 显示联机 README 文件的内容。 从可执行文件中去掉符号表,这样可以保护调试代码的能力。 为临时文件定义目录。 控制编译器冗长。 增加堆栈溢出的运行时检查。 打印诸如定义、定义及未定义的位置和已使用的位置的宏信息。 仅检查语法和语义错误。 显示编译器选项汇总列表。 关闭增量式链接程序。 打开增量式链接程序。 对 32 位体系结构到 64 位体系结构的移植过程中的常见问题发出警告。 允许在用 dbx 调试时不包括对象 (.o) 文件。 为源码浏览器生成表信息。

仅生成源码浏览器信息,而不进行编译。

3.3.4

浮点选项
以下浮点选项按字母顺序列出。
表 3-5 选项

浮点选项
操作

-fns[={no|yes}] -fprecision=p -fround=r -fsimple=n -fstore

(SPARC) 禁用或启用 SPARC 的非标准浮点模式。 x86:设置浮点精度模式。 设置启动时生效的 IEEE 舍入模式。 设置浮点优化首选项。 x86:强制浮点表达式的精度。

3-4

C++ 用户指南 ? 2005 年 1 月

表 3-5 选项

浮点选项 ( 续 )
操作

-ftrap=tlst -nofstore -xlibmieee

设置启动时生效的 IEEE 捕获模式。 x86:禁用表达式的强制精度。 在异常情况下,使 libm 返回数学例程的 IEEE 754 值。

3.3.5

语言选项
以下语言选项按字母顺序列出。
表 3-6 选项

语言选项
操作

-compat -features=alst -xchar -xldscope -xthreadvar -xtrigraphs -xustr

设置编译器的主发行版本兼容模式。 启用或禁用各种 C++ 语言特性。 在 char 类型定义为无符号的系统上,简化代码的移植。 控制变量和函数定义的缺省链接程序范围,以创建更快更安全的共 享库。 (SPARC) 更改缺省的线程局部存储访问模式。 启用三字母序列的识别。 启用识别由 16 位字符构成的字符串文字。

3.3.6

库选项
以下库链接选项按字母顺序列出。
表 3-7 选项

库选项
操作

-Bbinding –d{y|n} -G -Dname -i -Ldir

请求符号、动态或静态库链接。 允许或不允许整个可执行文件的动态库。 生成动态共享库来取代可执行文件。 为生成的动态共享库指定名称。 告知 ld(1) 忽略任何 LD_LIBRARY_PATH 设置。 将 dir 增加到搜索库的目录列表。

第3章

使用 C++ 参阅编译器选项

3-5

表 3-7 选项

库选项 ( 续 )
操作

-llib -library=llst -mt -norunpath -Rplst -staticlib=llst -xar -xbuiltin[=opt] -xia -xlang=l[,l] -xlibmieee -xlibmil -xlibmopt -xlic_lib=sunperf -xnativeconnect -xnolib -xnolibmil -xnolibmopt

将 liblib.a 或 liblib.so 增加到链接程序的库搜索列表。 强制将特定库和相关文件包含到编译和链接中。 编译和链接多线程代码。 不将库的路径生成到可执行文件中。 将动态库搜索路径生成到可执行文件中。 说明哪些 C++ 库是静态链接的。 创建归档库。 启用或禁用标准库调用的更多优化 (SPARC) 链接合适的区间运算库并设置适当的浮点环境。 包含适当的运行库,并确保指定语言的正确运行时环境。 在异常情况下,使 libm 返回数学例程的 IEEE 754 值。 内联选定的 libm 库例程用于进行优化。 使用优化的数学例程库。 (SPARC) 在 Sun Performance Library? 中的链接。请注意对于 C++, -library=sunperf 是链接该库的最佳方法。 包含对象文件中的接口信息和随后的共享库,使得这些共享库 可以与使用 Java? 编程语言编写的代码接口。 禁用与缺省系统库进行链接。 在命令行上取消 -xlibmil。 不使用数学例程库。

3.3.7

许可证选项
以下许可证选项按字母顺序列出。
表 3-8 选项

许可证选项
操作

-xlic_lib=sunperf -xlicinfo

(SPARC) 在 Sun Performance Library? 中的链接。请注意对于 C++, -library=sunperf 是链接该库的最佳方法。 显示许可证服务器信息。

3-6

C++ 用户指南 ? 2005 年 1 月

3.3.8

废弃的选项
以下选项已废弃或将要废弃。
表 3-9 选项

废弃的选项
操作

-library=%all -noqueue -ptr -vdelx -xprefetch=yes -xprefetch=no

废弃的选项,在以后的发行版本中将被删除。 禁用许可证队列。 编译器忽略。以后的编译器发行版本可以使用其他行为来重用该选项。 废弃的选项,在以后的发行版本中将被删除。 而是使用 -xprefetch=auto,explicit。 而是使用 -xprefetch=no%auto,no%explicit。

3.3.9

输出选项
以下输出选项按字母顺序列出。
表 3-10 选项

输出选项
操作

-c -dryrun -E -erroff -errtags -errwarn -filt -G -H -migration -o filename -P -Qproduce sourcetype -s

仅编译;生成对象 (.o) 文件,但禁止链接。 显示但不编译由驱动程序传递到编译器的选项。 仅在 C++ 源文件上运行预处理程序,并将结果发送至 stdout。 禁止编译器警告消息。 显示每条警告消息的消息标记。 如果指示的警告消息已发出,则 cc 以失败状态退出。 禁止编译器应用到链接程序错误消息的过滤。 生成动态共享库来取代可执行文件。 打印包含文件的路径名称。 解释可以从早期编译器获得有关移植信息的位置。 将输出文件或可执行文件的名称设置为 filename。 仅预处理源文件,输出到 .i 文件。 使 CC 驱动程序生成类型 sourcetype 的输出。 从可执行文件去掉符号表。

第3章

使用 C++ 参阅编译器选项

3-7

表 3-10 选项

输出选项 ( 续 )
操作

-verbose=vlst +w -w -xdumpmacros -xe -xhelp=flags -xhelp=readme -xM -xM1

控制编译器冗长。 必要时打印附加警告。 禁止警告消息。 打印诸如定义、定义及未定义的位置和已使用的位置的宏信息。 仅对源文件进行语法和语义检查,但不生成任何对象或可执行代 码。 显示编译器选项汇总列表 显示联机 README 文件的内容。 输出 makefile 依存信息。 生成依存信息,但排除 /usr/include。 为源码浏览器生成表信息。

-xsb -xsbfast -xtime -xwe -z arg

仅生成源码浏览器信息,而不进行编译。
报告每个编译阶段的执行时间。 通过返回非零的退出状态,将所有警告转换成错误。 链接程序选项。

3.3.10

运行时性能选项
以下运行时性能选项按字母顺序列出。
表 3-11 选项

运行时性能选项
操作

-fast -g -s -xalias_level -xarch=isa -xbuiltin[=opt] -xcache=c -xcg89

选择编译选项的组合以优化某些程序的执行速度。 指示编译器和链接程序准备程序以进行性能分析 (以及调试) 。 从可执行文件去掉符号表。 启用编译器执行基于类型的别名分析和优化。 指定目标架构指令集。 启用或禁用标准库调用的更多优化 (SPARC) 定义优化器的目标缓存属性。 为通用 SPARC 架构编译。

3-8

C++ 用户指南 ? 2005 年 1 月

表 3-11 选项

运行时性能选项 ( 续 )
操作

-xcg92 -xchip=c -xF -xinline=flst -xipo -xlibmil -xlibmopt -xlinkopt -xmemalign=ab -xnolibmil -xnolibmopt -xOlevel -xpagesize -xpagesize_heap -xpagesize_stack -xprefetch[=lst] -xprefetch_level -xprofile -xregs=rlst -xsafe=mem -xspace -xtarget=t -xthreadvar -xunroll=n -xvis

为 SPARC V8 架构编译。 指定目标处理器芯片。 启用函数和变量的链接程序重新排序。 指定用户编写的哪些例程可以被优化器内联 执行过程间优化。 内联选定的 libm 库例程用于进行优化。 使用优化数学例程的库。 (SPARC) 在对象文件中的任何优化上生成的可执行文件或动态库上, 执行链接时优化。 (SPARC) 指定最大的假定内存对齐和未对齐的数据访问行为。 在命令行上取消 -xlibmil。 不使用数学例程库。 将优化等级指定为 level。 (SPARC) 设置栈和堆所需的页面大小。 (SPARC) 设置堆所需的页面大小。 (SPARC) 设置栈所需的页面大小。 (SPARC) 在支持预取的架构上启用预取指令。 控制由于设置 -xprefetch=auto 而造成自动插入预取指令的攻击 性。 (SPARC) 使用运行时文件配置数据来收集或优化。 (SPARC) 控制临时寄存器的使用。 (SPARC) 允许不基于内存的自陷。 (SPARC) 不允许增加代码大小的优化。 指定目标指令集和优化系统。 (SPARC) 更改缺省的线程局部存储访问模式。 启用在可能的场合下解开循环。 (SPARC) 启用在 VISTM 指令集中定义的汇编语言模板的编译器识别

第3章

使用 C++ 参阅编译器选项

3-9

3.3.11

预处理程序选项
以下预处理程序选项按字母顺序列出。
表 3-12 选项

预处理程序选项
操作

-Dname[=def] -E -H -P -Uname -xM -xM1

将符号 name 定义为预处理程序。 仅在 C++ 源文件上运行预处理程序,并将结果发送至 stdout。 打印包含文件的路径名称。 仅预处理源文件,输出到 .i 文件。 删除预处理程序符号 name 的最初定义。 输出 makefile 依存信息。 生成依存信息,但排除 /usr/include。

3.3.12

文件配置选项
以下文件配置选项按字母顺序列出。
表 3-13 选项

文件配置选项
操作

-p -xa -xpg -xprofile

准备对象代码以使用 prof 收集文件配置的数据。 为文件配置生成代码。 使用 gprof 配置程序为文件配置编译。 (SPARC) 使用运行时文件配置数据来收集或优化。

3-10

C++ 用户指南 ? 2005 年 1 月

3.3.13

参考选项
以下选项提供了编译器信息的快速参考。
表 3-14 选项

参考选项
操作

-migration -xhelp=flags -xhelp=readme

解释可以从早期编译器获得有关移植信息的位置。 显示编译器选项汇总列表。 显示联机 README 文件的内容。

3.3.14

源文件选项
以下源文件选项按字母顺序列出。
表 3-15 选项

源文件选项
操作

-H -Ipathname -I-xM -xM1

打印包含文件的路径名称。 将 pathname 增加到 include 文件搜索路径。 更改包含文件搜索规则 输出 makefile 依存信息。 生成依存信息,但排除 /usr/include。

3.3.15

模板选项
以下模板选项按字母顺序列出。
表 3-16 选项

模板选项
操作

-instances=a -ptipath -template=wlst

控制模板实例的放置和链接。 为模板源文件指定附加搜索目录。 启用或禁用各种模板选项。

第3章

使用 C++ 参阅编译器选项

3-11

3.3.16

线程选项
以下线程选项按字母顺序列出。
表 3-17 选项

线程选项
操作

-mt -xsafe=mem -xthreadvar

编译和链接多线程代码。 (SPARC) 允许不基于内存的自陷。 (SPARC) 更改缺省的线程局部存储访问模式。

3-12

C++ 用户指南 ? 2005 年 1 月

第 II 部 分 编写 C++ 程序

第4章

语言扩展参阅
本章记录了特定于本编译器的语言扩展。附录 B 也提供了实现的特定信息。在命令行上 指定某些编译器选项之后,编译器才能识别本章中描述的某些特性。相关编译器选项在 相应章节中列出。 - features=extensions 选项使您能编译非标准代码,该代码通常可以被其他 C++ 编译 器接受。必须编译无效代码且不允许修改代码而使之有效时,您可以使用该选项。 本章描述了使用 -features=extensions 选项时编译器支持的语言扩展。

注 – 可以很容易的将每个支持无效代码的实例转变为所有编译器接受的有效代码。如 果允许使代码有效,那么您应该使代码有效而不是使用该选项。使用 -features= extensions 选项可以使某些编译器拒绝的无效代码永远存在。

4.1

链接程序作用域
使用以下声明说明符帮助约束外部符号的声明和定义。文件链接到共享库或可执行文件 之前,静态归档或对象文件指定的作用域限制不会生效。尽管如此,编译器仍然可以执 行显示链接程序作用域说明符的某些优化。 通过使用这些说明符,您不必再使用链接程序作用域的 mapfile。此外也可以通过在命令 行指定 -xldscope 来控制变量作用域的缺省设置。

4-1

有关更多信息请参阅第 A-98 页的第 A.2.129 节 “-xldscope={v}” 。
表 4-1 值

链接程序作用域声明说明符
含义

__global

符号定义具有全局链接程序作用域,并且是限制最少的链接程序作用 域。对符号的所有引用都绑定到定义符号的第一个动态装入模块中的定 义。该链接程序作用域是外部符号的当前链接程序作用域。 符号定义具有符号链接程序作用域,并且具有比全局链接程序作用域更多 的限制。将对链接的动态装入模块内符号的所有引用绑定到模块内定义的 符号。在模块外部,符号也显示为全局符号。该链接程序作用域对应于链 接程序选项 -Bsymbolic。尽管不能与 C++ 库一起使用 -Bsymbolic, 但是可以使用 __symbolic 说明符而不会引起问题。关于链接程序的更 多信息,请参阅 ld(1)。 符号定义具有隐藏的链接程序作用域。隐藏链接程序作用域具有比符号 和全局链接程序作用域更高的限制。将动态装入模块内的所有引用绑定 到该模块内的定义。符号在模块外部是不可视的。

__symbolic

__hidden

符号定义可以用更多限制的说明符来重新声明,但是不可以用较少限制的说明符重新声 明。符号定义后,不可以用不同的说明符声明符号。
__global 是限制最少的作用域, __symbolic 是限制较多的,而 __hidden 是限制最多

的作用域。 因为虚函数的声明影响虚拟表的结构和解释,所以所有虚函数对包括类定义的所有编译 单元必须是可视的。 因为 C++ 类可能需要生成诸如虚拟表和运行时类型信息等的隐式信息, 所以您可以将链 接程序说明符应用到结构、类和联合的声明及定义。在这种情况下,说明符后跟结构、 类或联合关键字。这种应用程序为其所有隐式成员隐含了相同的链接程序作用域。

4.2

线程局部存储
通过声明线程局部变量可以利用线程局部存储的优点。线程局部变量声明由具有附加声 明说明符 __thread 的普通变量声明组成。 有关更多信息请参阅第 A-142 页的第 A.2.168 节 “-xthreadvar[=o]” 。 必须将 __thread 说明符包括在线程变量的第一个声明中。用 __thread 说明符声明的 变量是没有 __thread 说明符时的边界。 只可以用 __thread 说明符声明静态持续时间的变量。具有静态持续时间的变量包括了 文件全局、文件静态、函数局部静态和类静态成员。不能用 __thread 说明符声明具有 动态或自动持续时间的变量。线程变量可以具有静态初始化函数,但是不可以具有动态

4-2

C++ 用户指南 ? 2005 年 1 月

初始化函数或析构函数。例如, __thread int x = 4; 是允许的,但是 __thread int x = f(); 是不允许的。线程变量不能包含具有重要构造函数和析构函数的类型。具体来讲,线程 变量不可以具有类型 std::string。 线程变量的地址运算符 (&) 在运行时求值并返回当前线程变量的地址。因此,线程变量 的地址不是常量。 线程变量的地址在相应线程的生命周期中是稳定的。进程中的任何线程可以在变量的生 命周期中自由使用线程变量的地址。不能在线程终止后使用线程变量地址。线程变量的 所有地址在线程终止后都是无效的。

4.3

用限制较少的虚函数覆盖
C++ 标准声称覆盖的虚函数在允许的异常中包含的限制并不比覆盖的任何函数少。该虚 函数可能与覆盖的任何函数具有相同或更多的限制。请注意,不存在异常规范也允许任 何异常。 例如,假定通过指向基类的指针调用函数。如果函数具有异常规范,则可以计算出没有 其他正在抛出的异常。如果覆盖函数具有限制较少的规范,则意外的异常可能会被抛 出,这会导致奇怪的程序行为并且终止程序。这就是规则的原因。 使用 -features=extensions 时,编译器允许使用限制较少的异常规范来覆盖函数。

4.4

生成 enum 类型和变量的向前声明
使用 -features=extensions 时,编译器允许 enum 类型和变量的向前声明。此外,编 译器允许声明不完整 enum 类型的变量。编译器总是假定不完整 enum 类型与当前平台 上的 int 类型具有相同的大小和范围。 以下两行代码是无效代码的示例,使用 -features=extensions 选项时将编译该无效代 码。
enum E; // 无效:不允许 enum 的向前声明 E e; // 无效:类型 E 不完整

因为 enum 定义不能互相引用,并且 enum 定义不能交叉引用另一种类型,所以 enum 类 型的向前声明从来都是不需要的。要使代码有效,可以在使用 enum 之前始终提供它的 完全定义。

第4章

语言扩展参阅

4-3

注 – 在 64 位架构上,enum 有可能需要比 int 类型更大的大小。如果是这种情况,并且 如果向前声明和定义在同一编译中是可视的,那么编译器将发出错误。如果实际大小不 是假定的大小并且编译器没有发现这个差异,那么代码将编译并链接,但有可能不能正 常运行。可能出现奇怪的程序行为,尤其是 8 字节值存储在 4 字节变量中时。

4.5

使用不完整 enum 类型
使用 -features=extensions 时,不完整的 enum 类型用作向前声明。例如,使用 -features=extensions 选项时,以下无效代码将编译。
typedef enum E F; // 无效, E 不完整

如前所述,在使用之前可以总是包括 enum 类型的定义。

4.6

将 enum 名称作为作用域限定符
因为 enum 声明不引入作用域,所以 enum 名称不能用为作用域限定符。例如,以下代码 是无效的。
enum E {e1, e2, e3}; int i = E::e1; // 无效:E 不是作用域名称

要编译该无效代码,请使用 -features=extensions 选项。 - features=extensions 选 项说明如果编译器的作用域限定符是 enum 类型的名称,那么忽略该限定符。 要使代码有效,请删除无效的限定符 E::。

注 – 使用该选项提高了排字错误的可能性,产生了编译没有错误消息的错误程序。

4-4

C++ 用户指南 ? 2005 年 1 月

4.7

使用匿名 struct 声明
匿名结构声明既不是声明结构标记的声明,也不是声明对象或 typedef 名称的声明。 C++ 中不允许匿名结构。
-features=extensions 选项允许使用匿名 struct 声明,但仅作为联合的成员。

以下代码是无效匿名 struct 声明的示例,该声明在使用 -features=extensions 选项 时编译。
union U { struct { int a; double b; }; // 无效:匿名结构 struct { char* c; unsigned d; }; // 无效:匿名结构 };

struct 成员的名称是可视的,没有 struct 成员名称的限定。在该编码示例中给出了 U

的定义,您可以这样编写:
U u; u.a = 1;

匿名结构与匿名联合服从相同的限制。

第4章

语言扩展参阅

4-5

注意,通过赋予每个 struct 名称而使代码有效,例如:
union U { struct { int a; double b; } A; struct { char* c; unsigned d; } B; }; U u; U.A.a = 1;

4.8

传递匿名类实例的地址
不允许获取临时变量的地址。例如,因为以下代码获取了构造函数调用创建的变量地 址,所以这些代码是无效的。不过,使用 -features=extensions 选项时,编译器接受 该无效代码。
class C { public: C(int); ... }; void f1(C*); int main() { f1( &C(2) ); // 无效 }

注意,可以通过使用显式变量来使该代码有效。
C c(2); f1(&c);

函数返回时,临时对象被销毁。程序员的责任就是确保不保留临时变量的地址。此外, 临时变量销毁时存储在临时变量 (例如 f1)中的数据会丢失。

4-6

C++ 用户指南 ? 2005 年 1 月

4.9

将静态名称空间作用域函数声明为类友元
下面的代码是无效的:
class A { friend static void foo(<args>); ... };

因为类名具有外部链接并且所有定义必须是相等的,所以友元函数也必须具有外部链 接。不过,使用 -features=extensions 选项时,编译器接受了该代码。 推测起来,程序员准备为该无效代码在类 A 的实现文件中提供非成员 “帮助程序”函 数。通过生成 foo 静态成员函数可以得到相同的效果。如果不要客户端调用函数,则可 以使该函数私有化。

注 – 如果使用该扩展,则类可以被任何客户端 “截获” 。任何客户端都可以包括类的头 文件,然后定义客户端自身的静态函数 foo,该函数自动成为类的友元。结果就好像是 您使类的所有成员成为了公共的。

4.10

使用函数名称的预定义 __func__ 符号
使用 -features=extensions 时,编译器将每个函数中的标识符 __func__ 隐式声明为 const char 的静态数组。如果程序使用标识符,那么编译器也在 function-name 是函数原 始名称的位置提供以下定义。类成员关系、名称空间和重载不反映在名称中。
static const char __func__[] = "function-name";

例如,请考虑以下代码段。
#include <stdio.h> void myfunc(void) { printf("%s\n", __func__); }

第4章

语言扩展参阅

4-7

每次调用函数时,函数将把以下内容打印到标准输出流。
myfunc

4-8

C++ 用户指南 ? 2005 年 1 月

第5章

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

5.1

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

5.1.1

可适应语言的头文件
可能需要开发能够包含在 C 和 C++ 程序中的头文件。 不过, 称为 “传统 C” 的 Kernighan 和 Ritchie C (K&R C)、ANSI C、Annotated Reference Manual C++ (ARM C++) 以及 ISO C++ 有时需要在单个头文件中对同一个程序元素给出不同的声明和定义。 (关于各个语 。 )要使头文件能够被所有这些标准接 言和版本的附加信息,请参阅 《C++ 迁移指南》 受,可能需要使用基于预处理程序宏 __STDC__ 和 __cplusplus 或其值的条件编译。 宏 __STDC__ 在 K&R C 中没有定义,但在 ANSI C 和 C++ 中都有定义。使用这个宏可 以从 ANSI C 或 C++ 代码中区分出 K&R C 代码。该宏最适用于从非原型函数定义中区 分原型函数定义。
#ifdef __STDC__ int function(char*,...); #else int function(); #endif

// C++ & ANSI C 声明 // K&R C

宏 __cplusplus 不在 C 中定义,但在 C++ 中定义。

5-1

注 – C++ 的早期版本定义的是宏 c_plusplus,而非 __cplusplus。宏 c_plusplus 现 在不再定义。
使用 __cplusplus 宏的定义来区分 C 和 C++。 这个宏在保护函数声明的 extern "C" 接 口规范时非常有用,如以下示例所示。为了防止不一致的 extern "C" 规范,请勿在 extern "C" 链接规范的作用域中放置 #include 指令。
#include “header.h” ... // ... 其他包括文件 ... #if defined(__cplusplus) extern “C” { #endif nt g1(); nt g2(); int g3() #if defined(__cplusplus) } #endif

在 ARM C++ 中, __cplusplus 宏值为 1。在 ISO C++ 中,宏的值为 199711L (标准年 。使用这个宏的值区分 ARM C++ 和 ISO C++。这个宏值在保护 月用 long 常量来表示) 模板语法的更改时极为有用。
// 模板函数规范 #if __cplusplus < 199711L int power(int,int); #else template <> int power(int,int); #endif

// ARM C++ // ISO C++

5.1.2

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

5-2

C++ 用户指南 ? 2005 年 1 月

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 #include “twice.h” int main() { return twice(-3); }

main.cc

注 – 使模板头文件幂等是非常重要的。 (请参阅第 5-2 页的第 5.1.2 节 “幂等头文 件” 。 )

第5章

程序组织

5-3

5.2.2

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

#ifndef TWICE_H #define TWICE_H template <class Number> Number twice(Number original); #endif TWICE_H template <class Number> Number twice( Number original ) { return original + original; } #include “twice.h” int main( ) { return twice<int>( -3 ); }

twice.cc

main.cc

模板定义文件 不得包括任何非幂等的头文件,而且通常根本不包括任何头文件。 (请参 阅第 5-2 页的第 5.1.2 节 “幂等头文件” 。 )注意,并非所有编译器都支持模板的独立定 义模型。 一个单独的定义文件作为头文件时,该文件可能会被隐式包括在许多文件中。因此,它 不应该包含任何函数或变量定义 (除非这些定义是模板定义的一部分)。一个单独的定 义文件可以包含类型定义,包括 typedef。

注 – 尽管模板定义文件的源文件通常会使用扩展名 (即, .c、 .C、 .cc、 .cpp、 .cxx ,但模板定义文件就是头文件。如果需要,编译器会自动包括这些它们。模 或 .c++) 板定义文件不可以单独编译。
如果将模板声明放置在一个文件中,而将模板定义放置在另一个文件中,则必须仔细考 虑如何构造定义文件,如何命名定义文件和如何放置定义文件。此外也需要向编译器显 式指定定义的位置。关于模板定义搜索规则的信息,请参阅第 7-7 页的第 7.5 节 “模板 定义搜索” 。

5-4

C++ 用户指南 ? 2005 年 1 月

第6章

创建和使用模板
有了模板,就可以采用类型安全方法参阅来编写适用于多种类型的单一代码。本章介绍 了模板的概念和函数模板上下文中的术语,讨论了更复杂的 (更强大的)类模板,描述 了模板的组成,此外还讨论了模板实例化、缺省模板参数和模板专门化。本章的结尾部 分讨论了模板的潜在问题。

6.1
6.1.1

函数模板
函数模板描述了仅用参数或返回值的类型来区分的一组相关函数。

函数模板声明
使用模板之前,请先声明。以下示例中所示的声明提供了使用模板的足够信息,但没有 提供实现模板的足够信息。
template <class Number> Number twice( Number original );

指定了模板描述的函数范围。 尤其特别的是 Number 是 在本示例中,Number 是模板参数, 模板类型参数,该参数在模板定义中的使用代表了在使用模板的位置所决定的类型。

6-1

6.1.2

函数模板定义
如果要声明模板,请先定义该模板。定义提供了实现模板的足够信息。以下示例定义了 在前一个示例中声明的模板。
template <class Number> Number twice( Number original ) { return original + original; }

因为模板定义通常出现在头文件中,所以模板定义必须在多个编译单元中重复。不过所 有的定义都必须是相同的。这种约束称为单次定义规则。 编译器不支持函数参数列表中非类型模板参数的表达式,如以下示例所示。
// 具有非类型模板参数的表达式 // 在函数参数列表中是不支持的 template<int I> void foo( mytype<2*I> ) { ... } template<int I, int J> void foo( int a[I+J] ) { ... }

6.1.3

函数模板用法
声明后,模板可以像其他函数一样使用。模板的使用由命名模板和提供函数参数组成。 编译器可以从函数参数类型推断出模板类型参数。例如,您可以使用上面声明的模板, 具体步骤如下所示。
double twicedouble( double item ) { return twice( item ); }

如果模板参数不能从函数参数类型推断出,则调用函数时必须提供模板参数。例如:
template<class T> T func(); // 无函数参数 int k = func<int>(); // 显式提供模板参数

6.2

类模板
类模板描述了一组相关的类或数据类型,它们只能通过类型来区分:整数值、指向 (或 引用)具有全局链接的变量的指针、其他的组合。类模板尤其适用于描述通用但类型安 全的数据结构。

6-2

C++ 用户指南 ? 2005 年 1 月

6.2.1

类模板声明
类模板声明仅提供了类的名称和类的模板参数。这种声明是不完整的类模板。 以下示例是命名为 Array 类的模板声明,该类可以将任何类型作为参数。
template <class Elem> class Array;

该模板是命名为 String 类的模板,该类将 unsigned int 作为参数。
template <unsigned Size> class String;

6.2.2

类模板定义
类模板定义必须声明类数据和函数成员,如以下示例所示。
template <class Elem> class Array { Elem* data; int size; public: Array( int sz ); int GetSize( ); Elem& operator[]( int idx ); };

template <unsigned Size> class String { char data[Size]; static int overflows; public: String( char *initial ); int length(); };

与函数模板不同,类模板可以同时具有类型参数(例如 class Elem)和表达式参数(例 。表达式参数可以是: 如 unsigned Size)
■ ■ ■ ■

具有整数类型或枚举的值 指向对象的指针或到对象的引用 指向函数的指针或到函数的引用 指向类成员函数的指针

第6章

创建和使用模板

6-3

6.2.3

类模板成员定义
类模板的完整定义需要类模板函数成员和静态数据成员的定义。动态 (非静态)数据成 员由类模板声明完全定义。

6.2.3.1

函数成员定义
模板函数成员的定义由模板参数专门化后跟函数定义组成。函数标识符通过类模板的类 名称和模板参数来限定。以下示例说明了 Array 类模板的两个函数成员的定义,该模板 具有 template <class Elem> 的模板参数专门化。每个函数标识符都通过模板类名称和 模板参数 Array<Elem> 来限定。
template <class Elem> Array<Elem>::Array( int sz ) {size = sz; data = new Elem[size];} template <class Elem> int Array<Elem>::GetSize( ) { return size; }

该示例说明了 String 类模板的函数成员定义。
#include <string.h> template <unsigned Size> int String<Size>::length( ) {int len = 0; while (len < Size && data[len]!= '\0') len++; return len;} template <unsigned Size> String<Size>::String( char *initial ) {strncpy(data, initial, Size); if (length( ) == Size) overflows++;}

6.2.3.2

静态数据成员定义
模板静态数据成员的定义由后跟变量定义的模板参数专门化组成,在此处变量标识符通 过类模板名称和类模板实际参数来限定。
template <unsigned Size> int String<Size>::overflows = 0;

6-4

C++ 用户指南 ? 2005 年 1 月

6.2.4

类模板的用法
模板类可以在使用类型的任何地方使用。指定模板类包括了提供模板名称和参数的值。 以下示例中的声明创建了基于 Array 模板的 int_array 变量。变量的类声明和方法集 与 Array 模板中的类声明和方法集相同,不同之处是 Elem 替换为 int (请参阅第 6-5 页的第 6.3 节 “模板实例化” ) 。
Array<int> int_array( 100 );

本示例中的声明使用 String 模板创建 short_string 变量。
String<8> short_string( "hello" );

需要任何其他成员函数时,您可以使用模板类成员函数。
int x = int_array.GetSize( );

int x = short_string.length( );

6.3

模板实例化
模板实例化包含了为模板参数的特定组合生成固定类或函数 (实例)。例如,编译器生 通过替换模板类定义中模板参数的 成了 Array<int> 的类和 Array<double> 的不同类。 模板参数,可以定义这些新的类。在上一节 “类模板”所述的 Array<int> 示例中,编 译器在 Elem 出现的位置替换 int。

6.3.1

隐式模板实例化
使用模板函数或模板类时需要实例。如果这种实例还不存在,则编译器隐式实例化模板 参数组合的模板。

第6章

创建和使用模板

6-5

6.3.2

显式模板实例化
编译器仅为实际使用的那些模板参数组合而隐式实例化模板。该方法不适用于构造提供 模板的库。 C++ 提供了显式实例化模板的功能,如以下示例所示。

6.3.2.1

模板函数的显式实例化
要显式实例化一个模板函数, template 关键字的后面应跟有该函数的声明 (而不是定 义);其中,函数识别符后面跟有模板参数。
template float twice<float>( float original );

在编译器可以推断出模板参数时,模板参数可以省略。
template int twice( int original );

6.3.2.2

模板类的显式实例化
要显式实例化模板类,请在 template 关键字后跟类的声明 (无定义);而要实例化类 标识符,请后跟模板参数。
template class Array<char>;

template class String<19>;

显式实例化类时,所有的类成员也必须实例化。

6.3.2.3

模板类函数成员的显式实例化
要显式实例化模板类函数成员,请在 template 关键字后跟函数的声明 (无定义);而 要实例化由模板类限定的函数标识符,则请后跟模板参数。
template int Array<char>::GetSize( );

template int String<19>::length( );

6-6

C++ 用户指南 ? 2005 年 1 月

6.3.2.4

模板类静态数据成员的显式实例
要显式实例化模板类静态数据成员,请在 template 关键字后跟成员的声明 (无定义); 而要实例化由模板类限定的成员标识符,则请后跟模板参数。
template int String<19>::overflows;

6.4

模板组合
可以用嵌套方式使用模板。这种方式尤其适用于在通用数据结构上定义通用函数,与在 标准 C++ 库中相同。例如,模板排序函数可以通过一个模板数组类进行声明:
template <class Elem> void sort( Array<Elem> );

并定义为:
template <class Elem> void sort( Array<Elem> store ) {int num_elems = store.GetSize(); for ( int i = 0; i < num_elems-1; i++ ) for ( int j = i+1; j < num_elems; j++ ) if ( store[j-1] > store[j] ) {Elem temp = store[j]; store[j] = store[j-1]; store[j-1] = temp;}}

上述示例定义了预先声明的 Array 类模板对象上的排序函数。下一个示例说明了排序函 数的实际用法。
Array<int> int_array( 100 ); sort( int_array ); // 构造整型数组 // 排序该数组

第6章

创建和使用模板

6-7

6.5

缺省模板参数
您可以将缺省值赋予类模板 (但不是函数模板)的模板参数。
template <class Elem = int> class Array; template <unsigned Size = 100> class String;

如果模板参数具有缺省值,则该参数后的所有参数也必须具有缺省值。模板参数仅能具 有一个缺省值。

6.6

模板专门化
将模板参数的某些组合视为特殊的参数可以优化性能,如以下 twice 的示例所示。或 者,模板描述将无法处理一组可能的参数,如以下 sort 的示例所示。模板专门化允许 您定义实际模板参数给定组合的可选实现。模板专门化覆盖了缺省实例化。

6.6.1

模板专门化声明
使用模板参数的组合之前,您必须声明专门化。以下示例声明了 twice 和 sort 的专用实 现。
template <> unsigned twice<unsigned>( unsigned original );

template <> sort<char*>( Array<char*> store );

如果编译器可以明确决定模板参数,则您可以省略模板参数。例如:
template <> unsigned twice( unsigned original );

template <> sort( Array<char*> store );

6-8

C++ 用户指南 ? 2005 年 1 月

6.6.2

模板专门化定义
必须定义声明的所有模板专门化。下例定义了上一节中声明的函数。
template <> unsigned twice<unsigned>( unsigned original ) {return original << 1;}

#include <string.h> template <> void sort<char*>( Array<char*> store ) {int num_elems = store.GetSize(); for ( int i = 0; i < num_elems-1; i++ ) for ( int j = i+1; j < num_elems; j++ ) if ( strcmp( store[j-1], store[j] ) > 0 ) {char *temp = store[j]; store[j] = store[j-1]; store[j-1] = temp;}}

6.6.3

模板专门化使用和实例化
专门化与其他任何模板一样使用并实例化,除此以外,完全专用模板的定义也是实例 化。

6.6.4

部分专门化
在前一个示例中,模板是完全专用的。也就是说,模板定义了特定模板参数的实现。模 板也可以部分专用,这意味着只有某些模板参数被指定,或者一个或多个参数被限定到 某种类型。生成的部分专门化仍然是模板。例如,以下代码样本说明了主模板和该模板 的完全专门化。
template<class T, class U> class A {...}; // 主模板 template<> class A<int, double> {...}; // 专门化

以下代码说明了主模板部分专门化的示例。
template<class U> class A<int> {...}; // 示例 1 template<class T, class U> class A<T*> {...}; // 示例 2 template<class T> class A<T**, char> {...}; // 示例 3



示例 1 提供了第一个模板参数是 int 类型时的特殊模板定义。
第6章 创建和使用模板 6-9

■ ■

示例 2 提供了第一个模板参数是任何指针类型时的特殊模板定义。 示例 3 为第一个模板参数是指针到指针的任何类型而第二个模板参数是 char 类型时 的情况提供了特殊模板定义。

6.7
6.7.1

模板问题部分
本节描述了使用模板时会遇到的问题。

非本地名称解析和实例化
有时模板定义使用模板参数或模板本身未定义的名称。如此,编译器解决了封闭模板作 用域的名称,该模板可以在定义或实例化点的上下文中。名称可以在不同的位置具有不 同的含义,产生不同的解析。 名称解析比较复杂。因此,您不应该依赖除一般全局环境中提供的名称外的非本地名 称。也就是说,仅使用在任何地方都用相同方法声明和定义的非本地名称。在以下示例 中,模板函数 converter 使用了非本地名称 intermediary 和 temporary。这些名称在 use1.cc 和 use2.cc 中具有不同的定义,并且可能在不同编译器下产生不同的结果。为 了模板能可靠地工作,所有非本地名称 (该示例中的 intermediary 和 temporary)都 必须在任何地方具有相同的定义。
use_common.h // 通用模板定义 template <class Source, class Target> Target converter( Source source ) {temporary = (intermediary)source; return (Target)temporary;} typedef int intermediary; int temporary; #include "use_common.h" use2.cc typedef double intermediary; unsigned int temporary; #include "use_common.h"

use1.cc

非本地名称的一个常见的用法是模板内的 cin 和 cout 流。有时程序员要将流作为模板 参数传递,这时就要引用到全局变量。不过,cin 和 cout 在任何地方都必须具有相同的 定义。

6-10

C++ 用户指南 ? 2005 年 1 月

6.7.2

作为模板参数的本地类型
模板实例化系统取决于类型名称,等价于决定哪些模板需要实例化或重新实例化。因此 本地类型用作模板参数时,会导致严重的问题。小心在代码中也出现类似的问题。例如:
代码样例 6-1

本地类型用作模板参数问题的示例 template <class Type> class Array { Type* data; int size; public: Array( int sz ); int GetSize( ); }; template <class Type> Array<Type>::Array( int sz ) {size = sz; data = new Type[size];} template <class Type> int Array<Type>::GetSize( ) {return size;} #include "array.h" struct Foo {int data;}; Array<Foo> File1Data(10); #include "array.h" struct Foo {double data;}; Array<Foo> File2Data(20);

array.h

array.cc

file1.cc

file2.cc

在 file1.cc 中注册的 Foo 类型与在 file2.cc 中注册的 Foo 类型不同。以这种方法使 用本地类型会出现错误和不可预料的结果。

第6章

创建和使用模板

6-11

6.7.3

模板函数的友元声明
模板在使用之前必须先声明。模板的使用由友元声明构成,不是由模板的声明构成。实 际的模板声明必须在友元声明之前。例如,编译系统尝试链接以下示例中生成的对象文 件时,会生成未实例化 operator<< 函数的未定义错误。
代码样例 6-2

友元声明问题的示例 // 生成 operator<< 函数的未定义错误 #ifndef ARRAY_H #define ARRAY_H #include <iosfwd> template<class T> class array { int size; public: array(); friend std::ostream& operator<<(std::ostream&, const array<T>&); }; #endif

array.h

array.cc

#include <stdlib.h> #include <iostream> template<class T> array<T>::array() {size = 1024;} template<class T> std::ostream& operator<<(std::ostream& out, const array<T>& rhs) {return out <<’[’ << rhs.size <<’]’;}

main.cc

#include <iostream> #include "array.h" int main() { std::cout << "creating an array of int..." << std::flush; array<int> foo; std::cout << "done\n"; std::cout << foo << std::endl; return 0; }

6-12

C++ 用户指南 ? 2005 年 1 月

注意,因为编译器作为普通函数 (array 类的 friend)的声明读取以下代码,所以编 译期间不会出现错误消息。
friend ostream& operator<<(ostream&, const array<T>&);

因为 operator<< 实际上是模板函数,所以您需要在 template class array 的声明之 所以 array<T> 前提供模板声明。 不过, 因为 operator<< 具有 type array<T> 的参数, 的声明必须先于函数声明。文件 array.h 必须显示如下:
#ifndef ARRAY_H #define ARRAY_H #include <iosfwd> // 下面两行将 operator<< 声明为模板函数。 template<class T> class array; template<class T> std::ostream& operator<<(std::ostream&, const array<T>&); template<class T> class array { int size; public: array(); friend std::ostream& operator<< <T> (std::ostream&, const array<T>&); }; #endif

第6章

创建和使用模板

6-13

6.7.4

在模板定义内使用限定名称
C++ 标准需要具有限定名称的类型,该名称取决于用 typename 关键字显式标注为类型 名称的模板参数。即使编译器 “知道”应该是类型,也需要具有限定名称的类型。以下 示例中的注释说明了需要用 typename 关键字来限定名称的类型。
struct simple { typedef int a_type; static int a_datum; }; int simple::a_datum = 0; // 不是类型 template <class T> struct parametric { typedef T a_type; static T a_datum; }; template <class T> T parametric<T>::a_datum = 0; // 不是类型 template <class T> struct example { static typename T::a_type variable1; // 依存 static typename parametric<T>::a_type variable2; // 依存 static simple::a_type variable3; // 不依存 }; template <class T> typename T::a_type // 依存 example<T>::variable1 = 0; // 不是类型 template <class T> typename parametric<T>::a_type // 依存 example<T>::variable2 = 0; // 不是类型 template <class T> simple::a_type // 不依存 example<T>::variable3 = 0; // 不是类型

6.7.5

嵌套模板名称
由于 “>>”字符序列解释为右移运算符,因此在一个模板名称中使用另一个模板名称时 必须要非常小心。确保将邻近的 “>” 字符用至少一个空格分隔开。 例如,以下是形式错误的语句:
Array<String<10>> short_string_array(100); // >> = 右移

被解释为:
Array<String<10 >> short_string_array(100);

6-14

C++ 用户指南 ? 2005 年 1 月

正确的语法为:
Array<String<10> > short_string_array(100);

6.7.6

引用静态变量和静态函数
在模板定义中,编译器不支持引用在全局作用域或名称空间中声明为静态的对象或函 数。如果生成了多个实例,则每个示例引用到了不同的对象,因此违背了单次定义规则 (C++ 标准的 3.2 节) 。通常的失败指示是链接时丢失符号。 如果想要所有模板实例化共享单一对象,那么请使对象成为已命名空间的非静态成员。 如果想要模板类的每个实例化不同对象,那么请使对象成为模板类的静态成员。如果想 要模板函数每个实例化的不同对象,那么请使对象具有函数局部化属性。

6.7.7

在同一目录中使用模板生成多个程序
如果通过指定 -instances=extern 生成多个程序或库,那么建议在不同的目录中生成 这些程序或库。如果要在同一目录中生成多个程序,那么您需要清除不同生成程序之间 的系统信息库。这样可以避免出现任何不可预料的错误。更多信息请参阅第 7-6 页的第 7.4.4 节 “共享模板系统信息库” 。 考虑具有 make 文件 a.cc、 b.cc、 x.h 和 x.cc 的以下示例。注意该示例仅适用于指定 -instances=extern 时:
........ Makefile ........ CCC = CC all:a b a: $(CCC) -I. -instances=extern -c a.cc $(CCC) -instances=extern -o a a.o b: $(CCC) -I. -instances=extern -c b.cc $(CCC) -instances=extern -o b b.o clean: /bin/rm -rf SunWS_cache *.o a b

第6章

创建和使用模板

6-15

... x.h ... template <class T> class X { public: int open(); int create(); static int variable; };

... x.cc ... template <class T> int X<T>::create() { return variable ; } template <class T> int X<T>::open() { return variable ; } template <class T> int X<T>::variable = 1;

... a.cc ... #include "x.h" int main() { X<int> temp1; temp1.open(); temp1.create(); }

6-16

C++ 用户指南 ? 2005 年 1 月

... b.cc ... #include "x.h" int main() { X<int> temp1; temp1.create(); }

如果同时生成了 a 和 b,那么在两个生成之间增加 make clean。以下命令会引起错误:
example% make a example% make b

以下命令不会产生任何错误:
example% make a example% make clean example% make b

第6章

创建和使用模板

6-17

6-18

C++ 用户指南 ? 2005 年 1 月

第7章

编译模板
C++ 编译器在模板编译方面处理的工作参阅要比传统 UNIX 编译器处理的工作多。 C++ 编译器必须按需为模板实例生成对象代码。该编译器会使用模板系统信息库在多个独立 的编译间共享模板实例,此外还接受某些模板编译选项。编译器必须在各个源文件中定 位模板定义,并维护模板实例和主线代码之间的一致性。

7.1

冗余编译
给定标志 -verbose=template 后,C++ 编译器通知您模板编译期间的重要事件。相反, 给定缺省 -verbose=no%template 时,编译器不会通知您。+w 选项可以指示模板实例化 发生时其他潜在的问题。

7.2

系统信息库管理
CCadmin(1) 命令管理模板系统信息库。例如,程序中的更改会引起某些实例化过量,这 样会浪费存储空间。 CCadmin -clean 命令 (以前的 ptclean)清除所有实例化和有关

的数据。实例化仅在需要时才重新创建。

7.2.1

生成的实例
为了生成模板实例,编译器将内联模板函数看作内联函数。编译器像管理其他内联函数 一样管理这些内联模板函数,另外本章中的说明不适用于模板内联函数。

7-1

7.2.2

整个类实例化
编译器通常实例化独立于其他成员的模板类成员,因此编译器仅实例化程序中使用的成 员。仅用于调试器的方法会因此而不正常地实例化。 有两种方法确保调试成员可用于调试器。
■ ■

首先,编写使用模板类实例成员 (否则无用)的非模板函数,不需要调用该函数。 其次,使用 -template=wholeclass 编译器选项,通知编译器实例化模板类的所有非 模板非内联成员,前提是任何这些相同成员都是可实例化的。

ISO C++ 标准允许开发人员编写模板类,因为并不是所有成员都可以使用模板参数。只 要非法成员未被实例化,程序就仍然完好。 ISO C++ 标准库使用了这种技术。不过, -template=wholeclass 选项实例化所有成员,因此在使用有问题的模板参数来进行实 例化时就不能使用这种模板类。

7.2.3

编译时实例化
实例化是 C++ 编译器从模板创建可用的函数或对象的过程。 C++ 编译器使用了编译时 实例化,在编译对模板的引用时强制进行实例化。 编译时实例化的优点是:
■ ■ ■

调试更为容易——错误消息出现在上下文中,同时允许编译器完整回溯到引用点。 模板实例化始终保持最新。 包括链接阶段在内的总编译时间减少了。

如果源文件位于不同的目录或您使用了具有模板符号的库,则模板可以多次实例化。

7.2.4

模板实例的放置和链接
从 Sun C++ 编译器的 5.5 版本开始,实例将被放入特殊的地址区,并且链接程序可以识 别并丢弃重复实例。您可以指示编译器使用五个实例放置和链接方法之一:外部、静态、 全局、显式和半显式。


外部实例适用于大多数程序的开发,并且满足以下条件时可以达到最好的执行效率:
■ ■

程序中的实例集比较小,但是每个编译单元引用了实例较大的子集。 很少有在多于一个或两个编译单元中引用的实例。

■ ■

静态,废弃-见下文。 缺省的全局实例适用于所有程序的开发,并且当对象引用不同的实例时执行效率可以 达到最好。 显式实例适用于某些需精确控制的应用程序编译环境。



7-2

C++ 用户指南 ? 2005 年 1 月



半显式实例需要较少控制的编译环境,但是生成了较大的对象文件,并且有严格的使 用规则。

本节讨论了五种实例放置和链接方法。关于生成实例的更多信息,请参阅第 6-5 页的第 6.3 节 “模板实例化” 。

7.3

外部实例
对于外部实例方法,所有实例都放置在模板系统信息库中。编译器确保只有一个一致的 模板实例存在;这些实例既不是未定义的也不是多重定义的。模板仅在需要时才重新实 例化。对于非调试代码,使用 -instances=extern 时的所有对象文件的总计大小小于 使用 -instances=global 时的所有对象文件的总计大小。 模板实例接收系统信息库中的全局链接。实例使用外部链接从当前编译单元引用。

注 – 如果以不同的步骤编译和链接并为编译步骤指定了 -instance=extern,则也必须 为链接步骤指定 -instance=extern。
这种方法的缺点是更改程序或程序发生重大更改时必须清除缓存。缓存是并行编译的瓶 颈,因为使用 dmake 时每次只能有一个编译访问缓存。另外,每个目录内仅能生成一个 程序。 决定缓存中是否存在有效的模板实例比直接在主对象文件中创建实例 (如果需要,用完 后可以丢弃)要花费更长的时间。 使用 instances=extern 选项指定外部链接。 因为实例存储在模板系统信息库中,所以您必须使用 CC 命令将使用外部实例的 C++ 对 象链接到程序中。 如果要创建包含所有模板实例的库,则使用具有 -xar 选项的 CC 命令。不要使用 ar 命 令。例如:
example% CC -xar -instances=extern -o libmain.a a.o b.o c.o

更多信息请参阅第 16 章。

7.3.0.1

可能的缓存冲突
指定 -instance=extern 时,请勿允许在同一目录中运行不同的编译器版本,以避免可 能的缓存冲突。使用 -instances=extern 模板模型时,请考虑以下问题:

第7章

编译模板

7-3



请勿在同一目录中创建不相关的二进制文件。在目录中创建任何二进制文件(.o、.a、 .so,可执行程序)都应该是相关的,在所有对象、函数的名称中,将公共区输入到两 个或多个具有相同定义的对象文件。 在同一目录中同时运行多个编译是安全的,例如使用 dmake 时。与另外一个链接步骤 同时运行任何编译或链接步骤是不安全的。 “链接步骤”意味着创建库或可执行程序 的任何操作。确保 makefile 中的依存关系不允许任何内容与链接步骤以并行方式运 行。



7.3.1

静态实例
注 – -instances=static 选项已过时。没有任何理由再使用 -instances=static,因 为 -instances=global 现在向您提供了所有静态的优点而没有它的缺点。以前编译器 中提供的该选项用于克服 C++ 5.5 中不存在的问题。
对于静态实例方法,所有实例都被放置在当前编译单元内。因此,模板在每个重新编译 期间重新实例化;这些实例不保存到模板系统信息库。 这种方法的缺点是不遵循语言语义,并且会生成很大的对象和可执行文件。 实例接收静态链接。这些实例在当前编译单元外部是不可视的或不可用的。因此,模板 可以在多个对象文件中具有相同的实例化。因为多重实例产生了不必要的大程序,所以 对于不可能多重实例化模板的小程序可以使用静态链接。 静态实例的编译速度很快, 因此这种方法也适用于修复并继续方式的调试。 (请参阅《使 。 ) 用 dbx 调试程序》

注 – 如果您的程序取决于多个编译单元间的共享模板实例 (例如模板类或模板函数的 静态数据成员) ,请勿使用静态实例方法。否则程序会工作不正常。
使用 -instances=static 编译器选项指定静态实例链接。

7.3.2

全局实例
与以前的编译器发行版本不同,本版本的编译器无需保护全局实例的多个副本。 这种方法的优点是通常由其他编译器接受的不正确源代码也能在这种模式中接受。特别 的是,从模板实例内对静态变量的引用是不合法的,但通常是可以接受的。 这种方法的缺点是单个对象文件会很大,原因是多个文件中模板实例有多个副本。如果 您使用 -g 选项 (或没有该选项)编译调试的某些对象文件,那么很难预测是否可以获 得链接到程序中模板实例的调试或非调试版本。

7-4

C++ 用户指南 ? 2005 年 1 月

模板实例接收全局链接。这些实例在当前编译单元外部是可视的和可用的。 使用 -instances=global 选项 (缺省选项)指定全局实例。

7.3.3

显式实例
在显式实例方法中,仅为显式实例化的模板生成实例。隐式实例化不能满足该要求。实 例被放置在当前编译单元内。因此,模板在每个重新编译期间重新实例化,这些模板不 保存到模板系统信息库。 这种方法的优点是拥有最少的模板编译和最小的对象大小。 缺点是您必须手动执行所有的实例化。 模板实例接收全局链接。这些实例在当前编译单元外部是可视的和可用的。链接程序识 别并丢弃重复项目。 使用 -instances=explicit 选项指定显式实例。

7.3.4

半显式实例
使用半显式实例方法时,仅为显式实例化或模板体内隐式实例化的模板生成实例。那些 被显式创建实例所需要的实例将会自动生成。主线代码中隐式实例化不满足该要求。实 例被放置在当前编译单元内。因此,模板在每个重新编译期间重新实例化;生成的实例 接收全局链接且不会被保存到模板系统信息库中。 使用 -instances=semiexplicit 选项指定半显式实例。

7.4

模板系统信息库
模板系统信息库存储了多个独立编译之间的模板实例,因此模板实例仅当需要时才编 译。模板系统信息库包含了使用外部实例方法时模板实例化所需的所有非源文件。系统 信息库不用于其他种类的实例。

7.4.1

系统信息库结构
缺省情况下,模板系统信息库包含在名为 SunWS_cache 的缓存目录中。

第7章

编译模板

7-5

缓存目录包含在放置对象文件的目录中。您可以通过设置 SUNWS_CACHE_NAME 环境变量 更改缓存目录的名称。注意, SUNWS_CACHE_NAME 变量的值必须是目录名称,不能是路 径的名称。这是因为编译器自动将模板缓存目录放置到了对象文件目录下,因此编译器 已经具有了路径。

7.4.2

写入模板系统信息库
编译器必须存储模板实例时,编译器将模板实例存储在对应于输出文件的模板系统信息 库中。例如,以下命令行将对象文件写入 ./sub/a.o,并将模板实例写入 ./sub/SunWS_cache 内包含的系统信息库中。如果缓存目录不存在,且编译器需要实 例化模板,则编译器将创建目录。
example% CC -o sub/a.o a.cc

7.4.3

从多模板系统信息库读取
编译器从对应于编译器读取的对象文件的模板系统信息库读取。也就是说,以下命令行 从 ./sub1/SunWS_cache 和 ./sub2/SunWS_cache 读取,并且如果需要,则写入 ./SunWS_cache。
example% CC sub1/a.o sub2/b.o

7.4.4

共享模板系统信息库
系统信息库内的模板不能违反 ISO C++ 标准的单次定义规则。也就是说,使用所有的模 板时模板必须具有相同的源。违反该规则会产生不可预料的行为。 确保不违反该规则的最简单和最保守的方法是在任何一个目录内仅生成一个程序或库。 两个不相关的程序可以使用相同类型的名称或外部名称来表示不同的内容。如果程序共 享模板系统信息库,则模板定义会出现冲突,会产生不可预料的结果。

7.4.5

模板实例自动与 -instances=extern 一致
模板系统信息库管理器确保了指定 -instances=extern 时,系统信息库中的实例状态 与源文件保持一致并为最新。 (即打开调试) 编译, 则从数据库所需的文件也用 -g 编译。 例如, 如果源文件用 -g 选项

7-6

C++ 用户指南 ? 2005 年 1 月

此外,模板系统信息库会跟踪编译中的更改。例如,如果设置 -DDEBUG 标志以定义名称 DEBUG ,则数据库进行跟踪。如果在以后的编译中省略该标志,则编译器重新实例化设 置依存关系的这些模板。

7.5

模板定义搜索
使用独立定义模板组织时,模板定义在当前编译单元不可用,并且编译器必须搜索该定 义。本节描述了编译器如何找到定义。 定义搜索有点复杂且易于出错。因此如果可能,您应该使用定义包括模板文件组织。这 样有助于避免一起定义搜索。请参阅第 5-3 页的第 5.2.1 节 “包括的模板定义” 。

注 – 如果使用 -template=no%extdef 选项,则编译器不会搜索独立源文件。

7.5.1

源文件位置约定
无需某个选项文件提供特定的指示说明,编译器可以采用 Cfront 样式的方法来查找模 板定义文件。这种方法需要模板定义文件包含了与模板声明文件相同的基名。该方法也 需要模板定义文件位于当前 include 路径中。例如,如果模板函数 foo() 位于 foo.h 中, 则相匹配的模板定义文件应该命名为 foo.cc 或某些其他可识别的源文件扩展 (.C、 .c、 .cc、 .cpp、 .cxx 或 .c++) 。模板定义文件必须位于其中一个普通 include 目录 或与定义文件相匹配的头文件的相同目录中。

7.5.2

定义搜索路径
另外一种可以替代用 -I 选项设定标准搜索路径的方法是:您可以用选项 -pti directory 指定模板定义文件的搜索目录。多个 -pti 标志定义了多个搜索目录,即搜索路径。如 果您使用 -pti 目录,则编译器在该路径查找模板定义文件并忽略 -I 标志。因为 -pti 目录标志将源文件的搜索规则复杂化,所以使用 -I 选项代替 -pti 目录 选项。

7.5.3

诊断有问题的搜索
有时,编译器会生成一些莫名其妙的警告或错误消息,原因是它正在查找您并不打算进 行编译的文件。此问题通常是由于某个文件 (例如 foo.h) 包含模板声明,而另一个文 件 (例如 foo.cc)又隐式包含了该文件。

第7章

编译模板

7-7

如果头文件 foo.h 具有模板声明,则在缺省情况下,编译器搜索名为 foo 且带有 C++ 文件扩展名 (C、 c、 cc、 cpp、 .cxx 或 c++)的文件。如果找到这样的文件,编译器将 自动把它包含进来。有关这类搜索的详细信息,请参阅第 7-7 页的第 7.5 节 “模板定义 搜索” 。 如果您拥有文件 foo.cc,但是不想采用这种方式进行处理,则您有两个选择:
■ ■

更改 .h 或 .cc 文件的名称,以消除名称匹配。 通过指定 -template=no%extdef 选项禁止自动搜索模板定义文件。然后必须在代码 中显式包含所有模板定义,并且不能使用 “独立定义”模型。

7.6

模板选项文件
模板选项文件是用户提供的可选文件,包含了定位模板定义和控制实例重新编译所需的 选项。此外,选项文件提供了控制模板专门化和显式实例化的功能。不过,因为 C++ 现 在支持在源代码中声明专门化和显式实例化所需的语法,所以您不需使用这些功能。

注 – C++ 编译器以后发行的版本中将不支持模板选项文件。
并且位于 SunWS_cache 目录内。 该选项文件是包含了大 该选项文件名为 CC_tmpl_opt, 量条目的 ASCII 文本文件。条目由关键字后跟所需的文本组成并用 (;) 终止。尽管关键 字不能分割,但是条目可以跨越多行。

7.6.1

注释
注释以 # 字符开头,并扩展到行尾。注释内的文本被忽略。
# 行尾之前的注释文本被忽略。

7.6.2

包括
您可以通过包括选项文件,在多个模板数据库间共享选项文件。该功能尤其适用于生成 包含模板的库时。处理期间,指定的选项文件完整地包括在当前选项文件中。您可以具 有多个 include 语句并将这些语句放置在选项文件的任何位置。该选项文件也可以嵌 套。
include "options-file";

7-8

C++ 用户指南 ? 2005 年 1 月

7.6.3

源文件扩展名
编译器使用缺省 Cfront 式样源文件定位器机制,所以您可以指定不同源文件供编译器 进行搜索。格式为:
extensions "ext-list";

ext-list 是以空格分开的格式的有效源文件扩展名列表,例如:
extensions ".CC .c .cc .cpp";

当选项文件不存在该条目时,编译器搜索的有效扩展名为 .cc、.c、.cpp、.C, .cxx 和 .c++。

7.6.4

定义源位置
可以使用 definition 选项文件条目显式指定定义源文件的位置。在模板声明和定义 文件名称不遵循标准 Cfront 式样约定时,请使用定义条目。条目的语法为:
definition name in "file-1",[ "file-2" ..., "file-n"] [nocheck "options"];

name 字段表明了选项条目有效的模板。每个名称仅允许一个定义条目。这种名称必须是 简单的名称,限定的名称是不允许的。而且括号、返回类型和参数列表是不允许的。无 论返回类型或参数为何,只有名称本身计数。因此,定义条目可以应用到多个 (可能重 载)模板。 “file-n”列表字段指定包含模板定义的文件。文件的搜索使用了定义搜索路径。文件名 必须以引号 (" ") 封闭。因为简单模板名称可以引用到不同文件中定义的不同模板,或因 为单一模板可以在不同文件中定义,所以此时可以使用多个文件。例如,如果 func 在 三个文件中定义,则这三个文件必须在定义条目中列出。 nocheck 字段在本节的结尾处描述。 在以下示例中,编译器在 foo.cc 中定位并实例化模板函数 foo。这种情况下,定义条 目是缺省搜索中冗余的条目。
代码样例 7-1

冗余定义条目 template <class T> T foo(T t) {} definition foo in "foo.cc";

foo.cc CC_tmpl_opt

第7章

编译模板

7-9

以下示例说明了静态数据成员的定义和简单名称的用法。
代码样例 7-2

静态数据成员的定义和简单名称的用法 template <class T> class foo {static T* fooref;}; #include "foo.h" template <class T> T* foo<T>::fooref = 0 definition fooref in "foo_statics.cc";

foo.h foo_statics.cc

CC_tmpl_opt

为 fooref 的定义提供的名称是简单名称,而不是限定名称 (例如 foo::fooref)。定 义条目的原因是文件名称不是 foo.cc (或某些其他可识别的扩展名),并且不能使用 缺省 Cfront 式样搜索规则来定位。 以下示例说明了模板成员函数的定义。如示例所述,成员函数可以完全与静态成员初始 化程序一样处理。
代码样例 7-3

模板成员函数定义 template <class T> class foo {T* foofunc(T);}; #include “foo.h” template <class T> T* foo<T>::foofunc(T t) {} definition foofunc in "foo_funcs.cc";

foo.h foo_funcs.cc

CC_tmpl_opt

以下示例说明了两个不同源文件中模板函数的定义。
代码样例 7-4

不同源文件中模板函数的定义 template <class T> class foo { T* func( T t ); T* func( T t, T x ); }; #include "foo.h" template <class T> T* foo<T>::func(T t) {} #include "foo.h" template <class T> T* foo<T>::func(T t, T x) {} definition func in "foo1.cc", "foo2.cc";

foo.h

foo1.cc

foo2.cc

CC_tmpl_opt

在本示例中,编译器必须能够找到重载函数 func() 的两个定义。定义条目告知了编译 器查找对应函数定义的位置。

7-10

C++ 用户指南 ? 2005 年 1 月

有时某些编译标志更改时,并不需要重新编译。您可以使用 definition 选项文件条 目的 nocheck 字段来避免不必要的重新编译,该字段告知了编译器和模板数据库管理器 检查依存关系时忽略某些选项。如果不想要编译器因特定命令行标志的增加或减少而重 新实例化模板函数,则请使用 nocheck 标志。条目的语法为:
definition name in "file-1"[, "file-2" ..., "file-n"] [nocheck "options"];

选项必须用引号 (" ") 封闭。 在以下示例中,编译器在 foo.cc 中定位并实例化模板函数 foo。如果以后需要重新实 例化检查,则编译器将忽略 -g 选项。
代码样例 7-5

nocheck 选项 template <class T> T foo(T t) {} definition foo in "foo.cc" nocheck "-g";

foo.cc CC_tmpl_opt

7.6.5

模板专门化条目
直到最近, C++ 语言才不提供专门化模板的机制,因此每个编译器提供自己的机制。本 节描述了使用前一个版本的 C++ 编译器机制的模板专门化。仅在兼容模式中 (-compat[=4]) 支持该机制。
special 条目告知了编译器给定函数是专门化函数,并且编译器遇到该函数时不需实例 化。使用编译时实例化方法时,使用选项文件中的 special 条目来预先注册专门化。语 法为:

special 声明 ;

该声明是没有返回类型的合法 C++ 式样的声明。例如:
代码样例 7-6

special 条目 template <class T> T foo(T t) {}; #include "foo.h" special foo(int);

foo.h main.cc CC_tmpl_opt

第7章

编译模板

7-11

前面的选项文件通知了编译器模板函数 foo() 不应为类型 int 实例化, 还通知了专用版 本是由用户提供的。如果没有选项文件中的该条目,那么函数不必实例化并且可能会导 致错误:
代码样例 7-7

何时应使用 special 条目的示例 template <classT> T foo(T t) {return t + t;} #include "foo.h" int func() {return foo(10);} #include "foo.h" int foo(int i) {return i * i;} // 专门化 int main() {int x = foo(10); int y = func(); return 0;}

foo.h file.cc

main.cc

在前面的示例中,编译器编译 main.cc 时,因为编译器已找到定义,所以可以正确使用 专用版本的 foo。不过编译 file.cc 时,因为编译器不知道 foo 存在于 main.cc 中, 所以编译器实例化自身版本的 foo。在大多数情况下,该过程会在链接期间导致多重定 义符号,但是在某些情况下 (尤其是库),使用错误的函数会导致运行时错误。如果使 用专用版本的函数,您应该注册这些专门化。
special 条目可以重载,如以下示例所示:
代码样例 7-8

重载 special 条目 template <classT> T foo(T t) {} #include "foo.h" int foo(int i) {} char* foo(char* p) {} special foo(int); special foo(char*);

foo.h main.cc

CC_tmpl_opt

要专用化模板类,请将模板参数包括在 special 条目中:
代码样例 7-9

专门化模板类 template <class T> class Foo {... various members...}; #include "foo.h" int main() {Foo<int> bar; return 0;} special class Foo<int>;

foo.h main.cc

CC_tmpl_opt

7-12

C++ 用户指南 ? 2005 年 1 月

如果模板类成员是静态成员,则您必须将关键字 static 包括在专门化条目中:
代码样例 7-10

专用化静态模板类成员 template <class T> class Foo {public:static T func(T);}; #include "foo.h" int main() {Foo<int> bar; return 0;} special static Foo<int>::func(int);

foo.h main.cc

CC_tmpl_opt

第7章

编译模板

7-13

7-14

C++ 用户指南 ? 2005 年 1 月

第8章

异常处理
本章讨论了 C++ 编译器如何实现异常处理。更多信息请参阅第 11-2 页的第 11.2 节 “在 多线程程序中使用异常” 。更多关于异常处理的信息,请参阅 Bjarne Stroustrup 编著的 The C++ Programming Language 第三版 (Addison-Wesley, 1997) 。

8.1

同步和异步异常
异常处理设计用于仅支持同步异常,例如数组范围检查。术语同步异常意味着异常仅可 以来源于 throw 表达式。 C++ 标准支持具有终止模型的同步异常处理。终止意味着异常抛出后,控制不会返回到 抛出点。 异常处理没有设计用于直接处理诸如键盘中断等异步异常。不过,如果小心处理,在出 现异步事件时也可以进行异常处理。例如,要用信号进行异常处理工作,您可以编写设 置全局变量的信号处理程序,并创建另外一个例程来定期轮询该变量的值,当该变量值 发生更改时抛出异常。不能从信号处理程序抛出异常。

8.2

指定运行时错误
有五个与异常有关的运行时错误消息:
■ ■ ■ ■ ■

没有异常处理程序 未预料到的异常抛出 异常只能在处理程序中重新抛出 在解开堆栈时,析构函数必须处理自身的异常 内存不足

8-1

在运行时检测到错误时,错误消息显示了当前异常的类型和这五个错误消息之一。缺省 情况下,预定义的函数 terminate() 被调用,然后调用 abort()。 编译器使用异常规范中提供的信息来优化代码生成。例如,禁止不抛出异常的函数表条 目,而函数异常规范的运行时检查在任何可能的地方被消除。

8.3

禁用异常
如果知道程序中不使用异常,则可以使用编译器选项 -features=no%except 来禁止生 成支持异常处理的代码。该选项的使用可以稍微减小代码的大小,并能加快代码的执行 速度。不过,用禁用的异常编译的文件链接到使用异常的文件时,在用禁用的异常编译 的文件中的某些局部对象在发生异常时不会销毁。缺省情况下,编译器生成支持异常处 理的代码。通常都要启用异常,只有时间和空间的开销是考虑的重要因素时才禁止异 常。

注 – 因为 C++ 标准库 dynamic_cast 和缺省运算符 new 需要异常,所以在标准模式 (缺省模式)中编译时不能关闭异常。

8-2

C++ 用户指南 ? 2005 年 1 月

8.4

使用运行时函数和预定义的异常
标准头文件 <exception> 提供了 C++ 标准中指定的类和与异常相关的函数。仅在标准 模式 (编译器缺省模式,或使用选项 -compat=5)中编译时才可访问该头文件。以下摘 录了 <exception> 头文件声明。
// 标准头文件 <exception> namespace std { class exception { exception() throw(); exception(const exception&) throw(); exception& operator=(const exception&) throw(); virtual ~exception() throw(); virtual const char* what() const throw(); }; class bad_exception:public exception {...}; // 意外的异常处理 typedef void (*unexpected_handler)(); unexpected_handler set_unexpected(unexpected_handler) throw(); void unexpected(); // 终止处理 typedef void (*terminate_handler)(); terminate_handler set_terminate(terminate_handler) throw(); void terminate(); bool uncaught_exception() throw(); }

标 准类 exception 是 由 所 选语 言 结 构 或 C++ 标 准 库抛 出 的 所 有异 常 的 基类。类 型 exception 的对象可以被构造、复制,在不生成异常销毁。虚拟成员函数 what() 返回 了描述异常的字符串。

第8章

异常处理

8-3

为了与 C++ 发行版本 4.2 中所用的异常兼容,头文件 <exception.h> 也被提供用于标 准模式。该头文件允许转换到标准 C++ 代码,并包含了不是标准 C++ 部分的声明。您 可以按照开发计划的许可来更新代码以遵循 C++ 标准 (使用 <exception> 而不使用 <exception.h>) 。
// 头文件 <exception.h>,用于转换 #include <exception> #include <new> using std::exception; using std::bad_exception; using std::set_unexpected; using std::unexpected; using std::set_terminate; using std::terminate; typedef std::exception xmsg; typedef std::bad_exception xunexpected; typedef std::bad_alloc xalloc;

在兼容模式 (-compat[=4]) 中,头文件 <exception> 不可用,并且头文件 <exception.h> 引用到 C++ 发行版本 4.2 提供的相同头文件。头文件在这里不会重新 生成。

8.5

将异常与信号和 Setjmp/Longjmp 混合
只要 setjmp/longjmp 函数不交互,您就可以在发生异常的程序中使用这些函数。 使用异常和 setjmp/longjmp 的所有规则分别应用。此外,只要在 A 处抛出和在 B 处捕 获的异常具有相同的结果,从点 A 到点 B 的 longjmp 就是有效的。具体来讲,您不必 longjmp 进或出 try 块或 catch 块 (直接或间接) ,或 longjmp 超过自动变量或临时变 量的初始化或不常用销毁。 不能从信号处理程序抛出异常。

8.6

生成具有异常的共享库
永远不要使用具有包含 C++ 代码的程序的 -Bsymbolic,相反要使用链接程序映射文 件。使用 -Bsymbolic 时,在不同模块中的引用可以绑定到被假设为全局对象内容的不 同副本中。

8-4

C++ 用户指南 ? 2005 年 1 月

异常机制依赖对地址的比较。如果您具有某项内容的两个副本,它们的地址就不等同且 异常机制可能失败,这是由于异常机制依赖对假设为唯一地址内容的比较。 使用 dlopen 打开共享库时,必须将 RTLD_GLOBAL 用于异常。

第8章

异常处理

8-5

8-6

C++ 用户指南 ? 2005 年 1 月

第9章

类型转换操作
本 章 讨 论 C++ 标 准 中 较 新 的 类 型 转 换 操 作 符:const_cast、 reinterpret_cast、 static_cast 和 dynamic_cast。类型转换可以将对象或值从一种类型转换为另一种类 型。 这些类型转换操作比以前的类型转换操作更好控制。 dynamic_cast<> 操作符提供了一 种方法来检查到多态类的指针的实际类型。您可以用文本编辑器来搜索所有新样式的类 ,而查找旧样式的类型转换需要语法分析。 型转换 (搜索 _cast) 否 则,新 的 类 型 转 换 全 部 执 行 传 统 类 型 转 换 符 号 允 许 的 类 型 转 换 子 集。例 如,
const_cast<int*>(v) 可以写为 (int*)v。 新的类型转换仅将各种可用的操作分类以更

清楚地表示您的意图,并允许编译器提供更完善的检查。 类型转换操作符是始终启用的。类型转换符不能被禁用。

9-1

9.1

const_cast
表达式 const_cast<T>(v) 可用于更改指针或引用的 const 或 volatile 限定符。 (在 新样式的类型转换中,只有 const_cast<> 可以去掉 const 限定符。) T 必须是指针、 引用或指向成员的指针的类型。
class A { public: virtual void f(); int i; }; extern const volatile int* cvip; extern int* ip; void use_of_const_cast( ) { const A a1; const_cast<A&>(a1).f( ); // 去掉 const ip = const_cast<int*> (cvip); // 去掉 const 和 volatile }

9.2

reinterpret_cast
表达式 reinterpret_cast<T>(v) 更改了表达式 v 值的解释。该表达式可用于在指针和 整数类型之间,在不相关的指针类型之间,在指向成员的指针类型之间,和在指向函数 的指针类型之间转换类型。
reinterpret_cast 操作符的用法可以具有未定义的或依赖于实现的结果。以下几点描 述了唯一确定的行为:


指向数据对象或函数的指针(但不是指向成员的指针)可以转换为足够包含该指针的 ) 任何整数类型。 (long 类型总是足以包含 C++ 编译器支持的体系结构上的指针值。 转换回原始类型时,指针值将与原始指针值相比较。 指向 (非成员)函数的指针可以转换为指向不同 (非成员)函数类型的指针。如果 转换回原始类型,指针值将与原始指针相比较。 假设新类型的对齐要求没有原始类型严格,则指向对象的指针可以转换为指向不同对 象类型的指针。转换回原始类型时,指针值将与原始指针值相比较。 如果使用重新解释的类型转换将 “指向 T1 的指针”类型的表达式转换为 “指向 T2 的指针”类型的表达式,则 T1 类型左值可以转换为 “对 T2 的引用”类型。







9-2

C++ 用户指南 ? 2005 年 1 月



如果 T1 和 T2 都是函数类型或都是对象类型,则可以将 “指向 T1 类型 X 成员的指 针”类型右值显式转换为 “指向 T2 类型 Y 成员的指针”类型右值。 在所有允许的情况下,空指针类型转换为不同的空指针类型后仍然是空指针。
reinterpret_cast 操作符不能用于转换 const,转换 const 要使用 const_cast。 reinterpret_cast 操作符不能用于转换指向位于同一类分层结构中不同类的指针, 需使用静态或动态类型转换来实现这一目的。 (reinterpret_cast 不执行所需的调 整。 )这一点在以下示例中描述:

■ ■ ■

class A {int a; public:A();}; class B:public A {int b, c;}; void use_of_reinterpret_cast( ) { A a1; long l = reinterpret_cast<long>(&a1); A* ap = reinterpret_cast<A*>(l); // 安全 B* bp = reinterpret_cast<B*>(&a1); // 不安全 const A a2; ap = reinterpret_cast<A*>(&a2); // 错误,没有 const }

9.3

static_cast
表达式 static_cast<T>(v) 将表达式 v 的值转换为 T 类型。该表达式可用于任何隐式 允许的转换类型。此外,任何值都可以转换为 void,并且如果类型转换与旧样式一样合 法,则任何隐式转换都可以反向转换。
class B {...}; class C:public B {...}; enum E {first=1, second=2, third=3}; void use_of_static_cast(C* c1) { B* bp = c1; // 隐式转换 C* c2 = static_cast<C*>(bp); // 反向隐式转换 int i = second; // 隐式转换 E e = static_cast<E>(i); // 反向隐式转换 }

static_cast 操作符不能用于转换 const。您可以使用 static_cast 来 “向下”转换 分层结构 (从基到派生的指针或引用) ,但是不检查转换,因此结果有可能无法使用。 static_cast 不能用于从虚拟基类向下转换。

第9章

类型转换操作

9-3

9.4

动态类型转换
指向类的指针 (或引用)可以实际指向 (引用)从该类派生的任何类。有时希望指向完 全派生类的指针,或指向完整对象的某些其他子对象。动态类型转换可以实现这些功 能。

注 – 用兼容模式 (-compat[=4]) 编译时,如果程序使用动态类型转换,您必须用 -features=rtti 编译。
动态类型转换将指向一个类 T1 的指针 (或引用)转换到指向另一个类 T2 的指针 (引 ,并且转换必 用) 。T1 和 T2 必须位于同一分层结构,类必须是可访问的(经公共派生) 须要是明确的。此外,除非转换是从派生的类到该派生类的一个基类,包括 T1 和 T2 的 分层结构的最小部分必须是多态的 (至少具有一个虚函数) 。 在表达式 dynamic_cast<T>(v) 中, v 是要被类型转换的表达式,而 T 是要转换到的类 T 必须是完整类的类型 (其中一个的定义是可视的) 的指针或引用, 或指向 cv void、 型。 const、 volatile 或 const volatile 的指针,其中 cv 是空字符串。

9.4.1

将分层结构向上类型转换
向上类型转换分层结构时,如果 T 指向 (或引用)由 v 指向 (引用)类型的基类,则该 转换等价于 static_cast<T>(v)。

9.4.2

类型转换到 void*
如果 T 是 void*,则该结果是指向完整对象的指针。也就是说, v 可能指向某些完整对 象的其中一个基类。在这种情况下, dynamic_cast<void*>(v) 的结果如同将分层结构 向下转换 v 到完整对象的类型,然后转换到 void*。 。 类型转换到 void* 时,分层结构必须是多态的 (具有虚函数)

9.4.3

将分层结构向下或交叉类型转换
向下或交叉类型转换分层结构时,分层结构必须是多态的 (具有虚函数)。结果在运行 时检查。

9-4

C++ 用户指南 ? 2005 年 1 月

向下或交叉类型转换分层结构时,从 v 到 T 的转换并不总是可行的。例如,尝试的转换 可以是不明确的, T 可能不可访问,或 v 不能指向 (或引用)必要类型的对象。如果运 行时检查失败且 T 是指针类型,则类型转换表达式的值是 T 类型的空指针。如果 T 是引 用类型,什么都不会返回(C++ 中没有空引用) ,且标准异常 std::bad_cast 被抛出。 例如,此公共派生的示例成功:
#include <assert.h> #include <stddef.h> // 为空 class A {public:virtual void f();}; class B {public:virtual void g();}; class AB:public virtual A, public B {}; void simple_dynamic_casts( ) { AB ab; B* bp = &ab; // 无需类型转换 A* ap = &ab; AB& abr = dynamic_cast<AB&>(*bp); // 成功 ap = dynamic_cast<A*>(bp); assert( bp = dynamic_cast<B*>(ap); assert( ap = dynamic_cast<A*>(&abr); assert( bp = dynamic_cast<B*>(&abr); assert( }

ap bp ap bp

!= != != !=

NULL NULL NULL NULL

); ); ); );

第9章

类型转换操作

9-5

然而该示例失败,原因是基类 B 是不可访问的。
#include <assert.h> #include <stddef.h> // 为空 #include <typeinfo> class A {public:virtual void f() {}}; class B {public:virtual void g() {}}; class AB:public virtual A, private B {}; void attempted_casts( ) { AB ab; B* bp = (B*)&ab; // 中断保护所必需的 C 样式类型转换 A* ap = dynamic_cast<A*>(bp); // 失败, B 是不可访问的 assert(ap == NULL); try { AB& abr = dynamic_cast<AB&>(*bp); // 失败, B 是不可访问的 } catch(const std::bad_cast&) { return; // 此处捕获的失败引用类型转换 } assert(0); // 不应到此处 }

如果在一个单独的基类中存在虚拟继承和多重继承,则实际动态类型转换必须能够识别 出唯一的匹配。如果匹配不唯一,则类型转换失败。例如,假定有如下附加类定义:
class AB_B:public AB, class AB_B__AB:public AB_B, public B {}; public AB {};

9-6

C++ 用户指南 ? 2005 年 1 月

示例:
void complex_dynamic_casts( ) { AB_B__AB ab_b__ab; A*ap = &ab_b__ab; // 正确:找到唯一的静态 A AB*abp = dynamic_cast<AB*>(ap); // 失败:不明确 assert( abp == NULL ); // 静态错误:AB_B* ab_bp = (AB_B*)ap; // 不是动态类型转换 AB_B*ab_bp = dynamic_cast<AB_B*>(ap); // 动态转换正确 assert( ab_bp != NULL ); }

dynamic_cast 返回的空指针错误可用作两个代码体之间的条件,一个用于输入正确时 处理类型转换,而另一个用于输入错误时停止类型转换。

void using_dynamic_cast( A* ap ) { if ( AB *abp = dynamic_cast<AB*>(ap) ) { // abp 非空, // 因此 ap 是指向 AB 对象的指针 // 继续并使用 abp process_AB(abp);} else { // abp 为空, // 因此 ap 不是指向 AB 对象的指针 // 不使用 abp process_not_AB( ap ); } }

在兼容模式 (-compat[=4]) 中,如果运行时信息还未启用 -features=rtti 编译器选 项,则编译器将 dynamic_cast 转换到 static_cast 并发出警告。 如果禁用异常, 则编译器将 dynamic_cast<T&> 转换到 static_cast<T&> 并发出警告。 (如果发现转换在运行时无效,则需要抛出对引用类型的 dynamic_cast 异常。关于异常 的信息,请参阅第 8 章。 动 态 转 换 需 要 比 对 应 的 设 计 模 式 慢,例 如 虚 函 数 的 转 换。请 参 阅 Design Patterns:E lements of Reusable Object-Oriented Software , Erich Gamma 著 (Addison-Wesley, 1994)。

第9章

类型转换操作

9-7

9-8

C++ 用户指南 ? 2005 年 1 月

第 10 章

改善程序性能
采用编译器易于编译优化的方式编写函数,可以改善 C++ 函数的性能。有许多关于软件 性能的书籍,尤其是关于 C++。例如,请参阅 C++ Programming Style ,Tom Cargill 所著 (Addison-Wesley, 1992)、 Writing Efficient Programs , Jon Louis Bentley 所著 (PrenticeHall, 1982)、 Efficient C++: Performance Programming Techniques , Dov Bulka 和 David Mayhew 所 著 (Addison-Wesley, 2000) 以 及 Effective C++ - 50 Ways to Improve Your Programs and Designs,第二版, Scott Meyers 所著 (Addison-Wesley, 1998)。本章不重复 这些有价值的信息,而是讨论了主要影响 C++ 编译器的那些性能技术。

10.1

避免临时对象
C++ 函数经常会产生必须创建并销毁的隐式临时对象。对于重要的类,临时对象的创建 和销毁会占用很多处理时间和内存。 C++ 编译器消除了某些临时类,但是并不能消除所 有的临时类。 您编写的函数要将临时对象的数目减少到理解程序所需的最小数目。这些技术包括:使 用显式变量而不使用隐式临时对象,以及使用引用变量而不使用值参数。另外一种技术 是实现和使用诸如 += 这样的操作,而不实现和使用只包含 + 和 = 的操作。例如,下面 的第一行引入了 a + b 结果的临时对象,而第二行则不是。
T x = a + b; T x( a ) ; x += b ;

10-1

10.2

使用内联函数
使用扩展内联而不使用正常调用时,对小而快速的函数的调用可以更小更快速。反过 来,如果使用扩展内联而不建立分支,则对又长又慢的函数的调用会更大更慢。另外, 只要函数定义更改,就必须重新编译对内联函数的所有调用。因此,使用内联函数时要 格外小心。 在期望更改函数定义 而且 重新编译所有调用方很费时间时,请不要使用内联函数。否 则,如果扩展函数内联的代码比调用函数的代码少,或使用函数内联时应用程序执行速 度显著提高,那么可以使用内联函数。 编译器不能内联所有函数调用,因此使用最耗时的函数内联会需要某些源码的更改。使 用 +w 选项以了解何时不发生函数内联。在以下情况中,编译器将不会内联函数:


函数包含了复杂控制构造,例如循环、 switch 语句和 try/catch 语句。这些函数很少 多次执行复杂控制构造。要内联这种函数,请将函数分割为两部分,里边的部分包 含了复杂控制构造,而外边的部分决定了是否调用里边的部分。即使编译器可以内 联完整函数,从函数常用部分中分隔出不常用部分的这种技术也可以改善性能。 内联函数体又大又复杂。因为对函数体内其他内联函数的调用,或因为隐式构造函 数和析构函数调用 (通常发生在派生类的构造函数和析构函数中) ,所以简单函数 体可以非常复杂。对于这种函数,内联扩展很少提供显著的性能改善,所以函数一 般不内联。 内联函数调用的参数既大又复杂。对于内联成员函数调用的对象是内联函数调用的 自身这种情况,编译器特别敏感。要内联具有复杂参数的函数,只需将函数参数计 算到局部变量并将变量传递到函数。





10.3

使用缺省运算符
如果类定义不声明无参数的构造函数、复制构造函数、复制赋值运算符或析构函数,那 么编译器将隐式声明它们。它们都是调用的缺省运算符。类似 C 的结构具有这些缺省运 算符。编译器生成缺省运算符时,可以了解大量关于需要处理的工作和可以产生优良代 码的工作。这种代码通常比用户编写的代码的执行速度快,原因是编译器可以利用汇编 级功能的优点,而程序员则不能利用该功能的优点。因此缺省运算符执行所需的工作 时,程序不能声明这些运算符的用户定义版本。 缺省运算符是内联函数,因此内联函数不合适时不使用缺省运算符 (请参阅上一节) 。 否则,缺省运算符是合适的:


用户编写的无参数构造函数仅为构造函数的基对象和成员变量调用无参数构造函数。 有效的基元类型具有摬恢葱腥魏尾僮鲾无参数构造函数。 用户编写的复制构造函数仅复制所有的基对象和成员变量。



10-2

C++ 用户指南 ? 2005 年 1 月

■ ■

用户编写的复制赋值运算符仅复制所有的基对象和成员变量。 用户编写的析构函数可以为空。

某些 C++ 编程手册建议编写类的程序员始终定义所有的运算符, 以便该代码的任何读者 都能了解该程序员没有忘记考虑缺省运算符的语义。显然,该建议与以上讨论的优化有 冲突。这种冲突的解决方案是在代码中放置注释以表明类正使用缺省运算符。

10.4

使用值类
包括结构和联合在内的 C++ 类通过值来传递和返回。对于 Plain-Old-Data (POD) 类, C++ 编译器需要像 C 编译器一样传递结构。这些类的对象是直接传递的。对于用户定义 复制构造函数的类的对象,编译器需要构造对象的副本,将指针传递到副本,并在返回 后销毁副本。这些类的对象是 间接 传递的。编译器也可以选择介于这两个需求之间的 类。不过,该选择影响二进制的兼容性,因此编译器对每个类的选择必须保持一致。 对于大多数编译器,直接传递对象可以加快执行速度。这种执行速度的改善对于小值类 (例如复数和概率值)来说尤其明显。有时为了改善程序执行效率,您可以设计更可能 直接传递而不是间接传递的类。 在兼容模式 (-compat[=4]) 中,如果类具有以下任何一条,则间接传递该类:
■ ■ ■ ■ ■

用户定义的构造函数 虚函数 虚拟基类 间接传递的基 间接传递的非静态数据成员

否则,类被直接传递。 在标准模式 (缺省模式)中,如果类具有以下任何一条,则间接传递该类:
■ ■ ■ ■

用户定义的复制构造函数 用户定义的析构函数 间接传递的基 间接传递的非静态数据成员

否则,类被直接传递。

10.4.1

选择直接传递类
尽可能直接传递类:


只要可能,就使用缺省构造函数,尤其是缺省复制构造函数。

第 10 章

改善程序性能

10-3



尽可能使用缺省析构函数。缺省析构函数不是虚拟的,因此具有缺省析构函数的类 通常不是基类。 避免使用虚函数和虚拟基。



10.4.2

在不同的处理器上直接传递类
C++ 编译器直接传递的类 (和联合)与 C 编译器传递结构 (或联合)完全相同。不过, C++ 结构和联合在不同的架构上进行不同的传递。
表 10-1 架构

在不同架构上结构和联合的传递
描述

SPARC V7/V8 SPARC V9

通过在调用方内分配存储并将指针传递到该存储,传递并返回结构和联合。 (也就是说,所有的结构和联合都通过引用传递。 ) 不超过 16 个字节 (32 个字节)的结构在寄存器中传递。通过在调用方内分 配存储并将指针传递到该存储,联合和所有其他结构将被传递并返回。 (也 就是说,小的结构在寄存器中传递,而联合和大的结构通过引用传递。 )因 此,小值类与基元类具有相同的传递效率。 结构和联合通过在堆栈上分配空间并将参数复制到堆栈上来传递。通过在调 用方的帧中分配临时对象并作为隐式第一个参数传递临时对象的地址,返回 结构和联合。

x86 平台

10.5

缓存成员变量
访问成员变量是 C++ 成员函数的通用操作。 编译器必须经常从内存通过 this 指针装入成员变量。因为值通过指针装入,所以编译 器有时不能决定何时执行第二次装入或以前装入的值是否仍然有效。在这些情况下,编 译器必须选择安全但缓慢的方法,在每次访问成员变量时重新装入成员变量。 如下所示,可以通过在局部变量中显式缓存成员变量的值来避免不必要的内存重新装 入:
■ ■ ■

声明局部变量并使用成员变量的值初始化该变量。 在函数中成员变量的位置使用局部变量。 如果局部变量变化,那么将局部变量的最终值赋值到成员变量。不过,如果成员函 数在该对象上调用另一个成员函数,那么该优化会产生不可预料的结果。

当值位于寄存器中时,这种优化最有效,而这种情况也与基元类型相同。基于内存的值 的优化也会很有效,因为减少的别名使编译器获得了更多的机会来进行优化。

10-4

C++ 用户指南 ? 2005 年 1 月

如果成员变量经常通过引用 (显式或隐式)来传递,那么优化可能并没什么效果。 有时,类的目标语义需要成员变量的显式缓存,例如在当前对象和其中一个成员函数参 数之间有潜在别名时。例如:
complex& operator*= (complex& left, complex& right) { left.real = left.real * right.real + left.imag * right.imag; left.imag = left.real * right.imag + left.image * right.real; }

会产生不可预料的结果,前提是调用时使用:
x*=x;

第 10 章

改善程序性能

10-5

10-6

C++ 用户指南 ? 2005 年 1 月

第 11 章

生成多线程程序
本章解释了如何生成多线程程序。此外,还讨论了异常的使用,解释了如何在线程之间 共享 C++ 标准库对象,此外还描述了如何在多线程环境中使用传统 (旧的) iostream。 、 《Tools.h++ 用户指南》和 《标准 关于多线程的更多信息,请参阅 《多线程编程指南》 C++ 库用户指南》 。

11.1

生成多线程程序
C++ 编译器附带的所有库都是多线程安全的。如果需要生成多线程应用程序,或者需要 将应用程序链接到多线程库,那么您必须使用 -mt 选项来编译和链接程序。该选项将 -D_REENTRANT 传递给预处理程序,并将 -lthread 以正确的顺序传递给 ld。在兼容模 式 (-compat[=4]) 下, -mt 选项确保了 libthread 在 libC 之前被链接。在标准模式 (缺省模式)下, -mt 选项确保了 libthread 在 libCrun 之前被链接。 不要直接用 -lthread 链接应用程序,因为这将会引起 libthread 以错误的顺序链接。 以下示例显示了当编译和链接分开进行时,生成多线程应用程序的正确方法:
example% CC -c -mt myprog.cc example% CC -mt myprog.o

以下示例显示了生成多线程应用程序的错误方法:
example% CC -c -mt myprog.o example% CC myprog.o -lthread <- libthread 链接错误

11-1

11.1.1

表明多线程编译
您可以通过使用 ldd 命令来检查应用程序是否被链接到 libthread:
example% CC -mt myprog.cc example% ldd a.out libm.so.1 => /usr/lib/libm.so.1 libCrun.so.1 => /usr/lib/libCrun.so.1 libthread.so.1 => /usr/lib/libthread.so.1 libc.so.1 => /usr/lib/libc.so.1 libdl.so.1 => /usr/lib/libdl.so.1

11.1.2

与线程和信号一起使用 C++ 支持库
C++ 支持库 libCrun、 libiostream、 libCstd 和 libC 都是多线程安全的,但不是 async 安全的。这就是说在多线程应用程序中,支持库中可用的函数不能用于信号处理 程序。这样做的话将导致死锁状态。 在多线程应用程序的信号处理程序中使用下列内容是不安全的: ■ iostream ■ new 和 delete 表达式 ■ 异常

11.2

在多线程程序中使用异常
当前异常处理的实现方法是多线程安全的;一个线程中的异常不会干预其他线程中的异 常。不过,您不能使用异常来进行线程之间的通信;一个线程中抛出的异常不会被其他 线程捕获到。 每个线程都可以设置自己的 terminate() 或 unexpected() 函数。在一个线程中调用 set_terminate() 或 set_unexpected() 仅影响该线程的异常。 terminate() 的缺省 函数是主线程的 abort() 和其他线程的 thr_exit() (请参阅第 8-1 页的第 8.2 节 “指 定运行时错误” ) 。

11.2.1

线程取消
通过对 pthread_cancel(3T) 调用进行线程取消会导致堆栈中的自动(本地非静态)对 象的析构,除非指定 -noex 或 -features=no%except。

11-2

C++ 用户指南 ? 2005 年 1 月

pthread_cancel(3T) 使用与异常一样的机制。当一个线程被取消时,本地析构函数与 清除例程将交叉执行,该清除例程被用户注册为 pthread_cleanup_push()。在特定的

清除例程注册之后,函数调用的本地对象在例程执行前就被销毁了。

11.3

在线程之间共享 C++ 标准库对象
C++ 标准库 (libCstd -library=Cstd)是多线程安全的 (有些语言环境例外) ,这样 可以确保在多线程环境中库内部正常工作。但是,您仍需要将各个线程之间要共享的库 对象锁定起来。请参阅 setlocale(3C) 和 attributes(5) 手册页。 例如,如果实例化字符串,然后创建新的线程并使用引用将字符串传递给线程。因为要 在线程之间显示共享这个字符串对象,所以您必须锁定对于该字符串的写访问。 (库提 供的用于完成该任务的工具在下文中会有描述。 ) 另一方面,如果通过值将字符串传递给新的线程,即使两个不同的线程通过 Rogue Wave 的 "write on copy" 技术共享表示,也不必担心锁定。库将自动处理锁定。只有当要使对 象显式可用于多线程或在线程之间传递引用,以及使用全局或静态对象时,您才需要锁 定。 下文描述了 C++ 标准库内部使用的锁定 (同步)机制,该机制用于确保在多线程下出 现正确的行为。
_RWSTDMutex 和 _RWSTDGuard 这两个同步类提供了实现多线程安全的机制。 _RWSTDMutex 类在下列成员函数中提供了与平台无关的锁定机制:
■ ■

void acquire() -自己锁定,或在锁定之前处于阻塞状态。 void release() -自己解锁。

class _RWSTDMutex { public: _RWSTDMutex (); ~_RWSTDMutex (); void acquire (); void release (); };

第 11 章

生成多线程程序

11-3

_RWSTDGuard 类是公用包装类,其中封装有 _RWSTDMutex 类的对象。 _RWSTDGuard 对 象尝试在其构造函数中获取封装的互斥 (抛出 std::exception on error 派生的 ::thread_error 类型的异常) ,并在析构函数中释放互斥 (析构函数从不会抛出异 常) 。

class _RWSTDGuard { public: _RWSTDGuard (_RWSTDMutex&); ~_RWSTDGuard (); };

另外,您可以使用宏 _RWSTD_MT_GUARD(mutex) (以前的 _STDGUARD)在多线程生成中 有条件地创建 _RWSTDGuard 的对象。该对象保护代码块的其余部分,并在该代码块中定 义为可同时被多个线程执行。在单线程生成中,宏扩展到空表达式中。

11-4

C++ 用户指南 ? 2005 年 1 月

以下示例说明了这些机制的使用。
#include <rw/stdmutex.h> // // 多个线程共享的整数。 // int I; // // 用以同步更新为 I 的互斥。 // _RWSTDMutex I_mutex; // // 每次对 I 递增 1。直接使用 _RWSTDMutex。 // void increment_I () { I_mutex.acquire(); I++; I_mutex.release(); }

// 锁定互斥。 // 解锁互斥。

// // 每次对 I 递减 1。使用 _RWSTDGuard。 // void decrement_I () { _RWSTDGuard guard(I_mutex); // 获取 I_mutex 的锁定。 --I; // // 调用处于保护状态的析构函数时, I 的锁定被释放。 // }

11.4

在多线程环境中使用传统 iostream
本节描述了如何使用 libC 和 libiostream 库的 iostream 类在多线程环境中进行输入 输出操作 (I/O)。本节还提供了如何通过从 iostream 类派生来扩展库的功能。不过本节 并不是用 C++ 编写多线程代码的指南。

第 11 章

生成多线程程序

11-5

此处的讨论只适用于旧的 iostream (libC 和 libiostream) ,但不适用于 libCstd (即 新的 iostream,它是 C++ 标准库的一部分) 。 iostream 库允许其接口被多线程环境下的应用程序使用,而这些程序在运行支持的 Solaris 操作系统版本时使用多线程功能。如果应用程序使用以前版本库的单线程功能, 那么该应用程序不会受到影响。 如果库在线程环境中能正常工作,就将其定义为是多线程安全的。通常,此处的“正确” 多线程尝试 意味着所有的公用函数都是可重入的。iostream 库提供了对多线程的保护, 修改由多个线程共享的对象 (即 C++ 类的实例)状态。不过, iostream 对象的多线程 安全范围限制在执行对象公用成员函数的周期内。

注 – 应用程序不会因为使用 libC 库中的多线程安全对象而自动保证是多线程安全的。 仅当应用程序在多线程环境中能按预期执行时,才将其定义为是多线程安全的。

11.4.1

多线程安全的 iostream 库的组织
多线程安全的 iostream 库的组织与其他版本的 iostream 库稍有不同。库的输出接口 指的是 iostream 类的公共的和受保护的成员函数以及可用的基类集合,这一点与其他 版本的库相同,但各个版本的类分层结构是不同的。详细信息,请参阅第 11-11 页的第 11.4.2 节 “对 iostream 库进行接口更改” 。 表 11-1 中列出了 iostream 软件包中的核心类。 初始核心类以 unsafe_ 前缀来重命名。
表 11-1 类

iostream 初始核心类
描述

stream_MT streambuf unsafe_ios unsafe_istream unsafe_ostream unsafe_iostream

多线程安全类的基类。 缓冲区的基类。 该类包含各种流类通用的状态变量;例如,错误和格式化状态。 该类支持 streambuf 检索到的字符序列的有格式和无格式的转换。 该类支持存储在 streambuf 中的字符序列的有格式和无格式的转换。 该类合并 unsafe_istream 和 unsafe_ostream 类用于双向操作。

每个多线程安全类都是从基类 stream_MT 派生的。每个多线程安全类(除了 streambuf 外)也是从现有 unsafe_ 基类派生的。示例如下:
class streambuf:public stream_MT {...}; class ios:virtual public unsafe_ios, public stream_MT {...}; class istream:virtual public ios, public unsafe_istream {...};

11-6

C++ 用户指南 ? 2005 年 1 月

stream_MT 类提供了要求的互斥 (mutex) 锁定,以便每个 iostream 类都是多线程安全 的。此外还提供了用于动态启用和禁用锁定的功能,以便多线程安全的属性可以动态更 改。 unsafe_ 类中包括 I/O 转换和缓冲区管理的基本功能,加到库中的多线程安全只限 于派生类。每个类的多线程安全版本包含了与 unsafe_ base 类相同的受保护的和公共 的成员函数。多线程安全版本的类中每个成员函数都像包装器一样锁定对象,调用 unsafe_ base 类中的相同函数,然后解锁对象。

注 – streambuf 类不是从不安全的类派生的。 streambuf 的公共的和受保护的成员函 数是可以通过锁定而重入的。此外同时也提供了带有 _unlocked 后缀的不锁定版本。

11.4.1.1

公共转换例程
iostream 接口中增加了一组可重入的多线程安全的公共函数。用户指定的缓冲区被作 为每个函数的附加参数。这些函数如下所述:
表 11-2 功能

多线程安全的可重入公共函数
描述

char *oct_r (char *buf, int buflen, long num, int width) char *hex_r (char *buf, int buflen, long num, int width) char *dec_r (char *buf, int buflen, long num, int width) char *chr_r (char *buf, int buflen, long num, int width) char *form_r (char *buf, int buflen, long num, int width)

将指针返回到用八进制表示数字的 ASCII 字符串。非零宽 度假定为格式化的字段宽度。返回值不保证指向用户提供 缓冲区的开始部分。

将指针返回到用十六进制表示数字的 ASCII 字符串。非零 宽度假定为格式化的字段宽度。返回值不保证指向用户提 供缓冲区的开始部分。

将指针返回到用十进制表示数字的 ASCII 字符串。非零宽 度假定为格式化的字段宽度。返回值不保证指向用户提供 缓冲区的开始部分。

将指针返回到包含字符 chr 的 ASCII 字符串。 如果宽度非 零,那么字符串包含了后跟 chr 的 width 空格。返回值 不保证指向用户提供缓冲区的开始部分。

返回由 sprintf 格式化字符串的指针,使用格式字符串 format 和其他剩余的参数。缓冲区必须具有足够的空间 以包含格式化的字符串。

第 11 章

生成多线程程序

11-7

注 – 用来确保与早期版本 libC 兼容的 iostream 库的公共转换例程(oct、hex、dec、 chr 和 form)都不是多线程安全的。

11.4.1.2

使用多线程安全的 libC 库进行编译和链接
生成使用 libC 库的 iostream 类的应用程序以在多线程环境中运行时,请使用 -mt 选 项编译和链接应用程序的源代码。该选项将 -D_REENTRANT 传递给预处理程序,并将 -lthread 传递给链接程序。

注 – 使用 -mt (而不是 -lthread)来链接 libC 和 libthread。该选项确保了库的正 确链接顺序。不正确的使用 -lthread 会引起应用程序工作不正常。
使用 iostream 类的单线程应用程序不需要特殊的编译器或链接程序选项。缺省情况 下,编译器用 libC 库链接。

11.4.1.3

多线程安全的 iostream 限制
iostream 库的多线程安全的限制定义意味着大量使用 iostream 的编程用语在使用共 享 iostream 对象的多线程环境中是不安全的。

检查错误状态
要多线程安全,必须要对引起错误的 I/O 操作所在的临界区进行错误检查。以下示例说 明了如何检查错误:
代码样例 11-1

检查错误状态

#include <iostream.h> enum iostate {IOok, IOeof, IOfail}; iostate read_number(istream& istr, int& num) { stream_locker sl(istr, stream_locker::lock_now); istr >> num; if (istr.eof()) return IOeof; if (istr.fail()) return IOfail; return IOok; }

11-8

C++ 用户指南 ? 2005 年 1 月

在 该 示 例 中, stream_locker 对 象 sl 的 构 造 函 数 锁 定 了 istream 对 象 istr。在 read_number 终止时调用的析构函数 sl 解锁 istr。

获取通过上次未格式化输入操作提取的字符
要成为多线程安全的,必须在互斥使用 istream 对象的线程内,并在包括上次输入操作 和 gcount 调用的执行周期内,调用 gcount 函数。以下示例说明了对 gcount 的调用:
代码样例 11-2

调用 gcount

#include <iostream.h> #include <rlocks.h> void fetch_line(istream& istr, char* line, int& linecount) { stream_locker sl(istr, stream_locker::lock_defer); sl.lock(); // 锁定流 istr istr >> line; linecount = istr.gcount(); sl.unlock(); // 解锁 istr ... }

在该示例中, stream_locker 类的 lock 和 unlock 成员函数定义了程序中的互斥区域。

用户定义的 I/O 操作
要成为多线程安全的,为用户定义类型定义的 I/O 操作必须锁定以定义临界区 (这些类 型涉及到单个操作的特定顺序) 。以下示例说明了用户定义的 I/O 操作:
代码样例 11-3

用户定义的 I/O 操作

#include <rlocks.h> #include <iostream.h> class mystream:public istream { // 其他定义 ... int getRecord(char* name, int& id, float& gpa); }; int mystream::getRecord(char* name, int& id, float

搜索更多“Sun+Studio+10+C%2B%2B+用户指南”

网站地图

All rights reserved Powered by 伤城文章网 5xts.com

copyright ©right 2010-2021。
伤城文章网内容来自网络,如有侵犯请联系客服。zhit325@126.com