库提供了在多个应用程序间共享代码的方法,也提供了减小超大型应用程序复杂度的方法。C++ 编译器使您可以访问各种库。本章说明了如何使用这些库。
Oracle Solaris 操作系统附带了安装在 /usr/lib 中的几个库。这些库大多有 C 接口。缺省情况下,其中的 libc 和 libm 库通过 CC 驱动程序进行链接。如果使用 –mt 选项,则链接 libthread 库。如果要链接其他系统库,请在链接时使用适当的 –l 选项。例如,要链接 libdemangle 库,请在链接时在 CC 命令行上传递 –ldemangle:
example% CC text.c -ldemangle |
C++ 编译器具有自己的运行时支持库。所有 C++ 应用程序都由 CC 驱动程序链接到这些库。C++ 编译器还具有其他一些有用的库,如下节所述。
C++ 编译器附带了一些库。其中一些只能在兼容模式 (-compat=4) 下使用,有些只能在标准模式 (–compat=5) 下使用,有些可以在这两种模式下使用。libgc 和 libdemangle 库都有 C 接口,可以在任何模式下链接到应用程序。
下表列出了随 C++ 编译器提供的库,以及可以使用这些库的模式。
表 11–1 C++ 编译器附带的库
库 |
说明 |
可用模式 |
---|---|---|
libstlport |
标准库的 STLport 实现。 |
–compat=5 |
libstlport_dbg |
调试模式的 STLport 库 |
–compat=5 |
C++ 运行时 |
–compat=5 |
|
C++ 标准库 |
–compat=5 |
|
传统 iostream |
–compat=5 |
|
C++ 运行时,传统 iostream |
–compat=4 |
|
支持 -xia 选项 |
–compat=5 |
|
复数库 |
–compat=4 |
|
librwtool |
Tools.h++ 7 |
–compat=4、– compat=5 |
支持调试的 Tools.h++ 7 |
–compat=4、–compat=5 |
|
垃圾收集 |
C 接口 |
|
还原 |
C 接口 |
请勿重新定义或修改用于 STLport、Rogue Wave 或 Solaris Studio C++ 库的任何配置宏。库是按照适用于 C++ 编译器的方式进行配置和生成的。libCstd 和 Tool.h++ 配置为可互操作,因此,修改配置宏会导致程序不能编译、不能链接或不能正常运行。
以下是这些库中每个库的简单描述。
libCrun:该库包含了标准模式 (–compat=5) 下编译器所需的运行时支持,并提供了对 new/delete、异常及 RTTI 的支持。
libCstd:这是 C++ 标准库。具体来说,该库包含了 iostream。如果有使用传统 iostream 的现有源代码,而且要使用标准 iostream,必须修改源代码以符合新接口。有关详细信息,请参见《C++ 标准库参考》联机手册。
libiostream:这是使用 – compat=5 生成的传统 iostream 库。如果有使用传统 iostream 的源代码,且要在标准模式 (–compat=5) 下编译这些源代码,可以使用 libiostream 而不必修改源代码。可使用 – library=iostream 获取此库。
标准库的很大部分取决于使用的标准 iostream。在相同程序中使用传统的 iostream 可能会出现问题。
libC:这是兼容模式 (–compat=4) 下所需的库。该库包含了 C++ 运行时支持和传统 iostream。
libcomplex:该库提供了兼容模式 (-compat=4) 下的复数运算。在标准模式下,可使用 libCstd 中的复数运算功能。
libstlport:这是 C++ 标准库的 STLport 实现。可以通过指定选项 -library=stlport4,使用该库而非缺省的 libCstd。但不能在同一程序中同时使用 libstlport 和 libCstd。您必须使用其中之一编译和链接包括输入库在内的一切项目。
librwtool (Tools.h++):Tools.h++ 是来自 RogueWave 的 C++ 基础类库。提供了版本 7。该库已过时,不应在新代码中使用该库。提供它是为了支持针对使用 RW Tools.h++ 的 C++ 4.2 编写的程序。
libgc:该库用于部署模式或垃圾收集模式。只是与 libgc 库链接就会自动且永久修复程序的内存泄漏。虽然能以其他方式正常编程,但如果将程序与 libgc 库链接,则无需调用 free 或 delete 就可完成编程。垃圾收集库对动态装入库具有依赖性,因此在链接程序时要指定 -lgc 和 -ldl。
有关其他信息,请参见 gcFixPrematureFrees(3) 和 gcInitialize(3) 手册页。
libdemangle:该库用于还原 C++ 损坏名称。
与本节所述库关联的手册页位于第 1、3、3C++ 和 3cc4 节中。
要访问 C++ 库的手册页,请输入:
example% man library-name |
要访问 C++ 库版本 4.2 的手册页,请输入:
example% man -s 3CC4 library-name |
缺省情况下,CC 驱动程序会链接其中一些 C++ 库,而其他库需要显式链接。在标准模式下,CC 驱动程序缺省链接下列库:
-lC -lm -lc
有关更多信息,请参见A.2.49 -library=l[ ,l...]。
CC 驱动程序提供了一些选项来帮助用户使用库。
-mt 选项用于编译和链接多线程代码。
-xia 选项用于链接区间运算库。
-xlang 选项用于链接 Fortran 或 C99 运行时库。
-library 选项用于指定 Solaris Studio C++ 编译器附带的以下库:
libCrun
libCstd
libiostream
libC
libcomplex
libstlport、libstlport_dbg
librwtool、librwtool_dbg
libgc
要使用传统 iostream 形式的 librwtool,请使用 -library=rwtools7 选项。要使用标准 iostream 形式的 librwtool,请使用 -library=rwtools7_std 选项。
libstdcxx(作为 Solaris OS 的一部分分发)
以下命令动态链接传统 iostream 形式的 Tools.h++ 版本 7 和 libiostream 库。
example% CC test.cc -library=rwtools7,iostream |
以下命令静态链接 libgc 库。
example% CC test.cc -library=gc -staticlib=gc |
以下命令在兼容模式下编译 test.cc 并静态链接 libC。因为在兼容模式下缺省链接 libC,所以不必使用 – library 选项指定该库。
example% CC test.cc -compat=4 -staticlib=libC |
以下命令排除了库 libCrun 和 libCstd,否则缺省情况下这两个库包括在内。
example% CC test.cc -library=no%Crun,no%Cstd |
缺省情况下,CC 根据命令行选项链接不同的系统库集合。如果指定 -xnolib(或 -nolib),CC 仅链接在命令行上使用 -l 选项显式指定的那些库。(如果使用 -xnolib 或 -nolib,会忽略 -library 选项(如果有)。)
使用 –R 选项可以在可执行文件中生成动态库搜索路径。执行期间,运行时链接程序使用这些路径搜索应用程序所需的共享库。缺省情况下,CC 驱动程序将 – R<install_directory>/lib 传递给 ld(如果编译器安装在标准位置中)。可以使用 -norunpath 禁止在可执行文件中生成共享库的缺省路径。
缺省情况下,链接程序会搜索 /lib 和 /usr/lib。请勿在 -L 选项中指定这些目录或任何编译器安装目录。
对于针对部署生成的程序,应该使用 -norunpath 或 -R 选项进行生成,这样可避免在编译器目录中查找库。(请参见11.6 使用共享库)。
在源码中包括适当的头文件。
将程序与目标库链接。
传统 iostream。该术语是指 C++ 4.0、4.0.1、4.1 和 4.2 编译器以及早期基于 cfront 的 3.0.1 编译器附带的 iostream 库。没有用于此库的标准。在兼容模式下,该库是 libC 的一部分,在标准模式下,libiostream 中也有该库。
标准 iostream。这是 C++ 标准库 libCstd 的一部分,仅用于标准模式下。该库与传统 iostream 库的二进制和源码都不兼容。
如果已有 C++ 源,那么代码可能象以下示例一样使用传统 iostream。
// file prog1.cc #include <iostream.h> int main() { cout << "Hello, world!" << endl; return 0; } |
以下命令在兼容模式下编译 prog1.cc,并将其链接到名为 prog1 的可执行程序中。传统 iostream 库是 libC 的一部分,兼容模式下缺省链接该库。
example% CC -compat prog1.cc -o prog1 |
下一个示例使用标准 iostream。
// file prog2.cc #include <iostream> int main() { std::cout << "Hello, world!" << std::endl; return 0; } |
以下命令编译 prog2.cc 并将其链接到名为 prog2 的可执行程序中。该程序在标准模式下编译,且缺省链接包括标准 iostream 库的 libCstd。
example% CC prog2.cc -o prog2 |
有关编译模式的完整讨论,请参见《C++ 迁移指南》。
标准库提供了模板化的 complex 库,该库与 C++ 4.2 编译器提供的 complex 库类似。如果在标准模式下编译,必须使用 <complex> 而非 <complex.h>。不能在兼容模式下使用 <complex>。
在兼容模式下,必须在链接时显式请求 complex 库。在标准模式下,complex 库包括在 libCstd 中,缺省情况下链接该库。
标准模式下,没有 complex.h 头文件。在 C++ 4.2 中,"complex" 是类名称,但在标准 C++ 中,"complex" 是模板名称。不可能提供可使旧的代码不加修改就可工作的 typedef。因此,为使用复数的 4.2 版编写的代码需要某些简单的编辑,以便使用标准库。例如,以下代码是为 4.2 版编写的,并将在兼容模式下编译。
// file ex1.cc (compatibility mode) #include <iostream.h> #include <complex.h> int main() { complex x(3,3), y(4,4); complex z = x * y; cout << "x=" << x << ", y=" << y << ", z=" << z << endl; } |
以下示例在兼容模式下编译并链接 ex1.cc,然后执行该程序。
example% CC -compat ex1.cc -library=complex example% a.out x=(3, 3), y=(4, 4), z=(0, 24) |
下面将 ex1.cc 重写为 ex2.cc 以在标准模式下编译:
// file ex2.cc (ex1.cc rewritten for standard mode) #include <iostream> #include <complex> using std::complex; int main() { complex<double> x(3,3), y(4,4); complex<double> z = x * y; std::cout << "x=" << x << ", y=" << y << ", z=" << z << std::endl; } |
以下示例在标准模式下编译并链接重写的 ex2.cc,然后执行该程序。
% CC ex2.cc % a.out x=(3,3), y=(4,4), z=(0,24) |
关于使用复数运算库的更多信息,请参见表 13–4。
下表列出了链接 C++ 库的编译器选项。有关更多信息,请参见A.2.49 -library=l[ ,l...]。
表 11–2 链接 C++ 库的编译器选项
库 |
编译模式 |
选项 |
---|---|---|
–compat=5 |
不需要 -library=iostream |
|
complex |
–compat=4 -compat=5 |
-library=complex 不需要 |
–compat=4 –compat=5 |
-library=rwtools7 -library=rwtools7_std |
|
Tools.h++ 版本 7 调试 |
–compat=4 –compat=5 |
-library=rwtools7_dbg -library=rwtools7_dbg,iostream -library=rwtools7_std_dbg |
–compat=4 –compat=5 |
-library=gc -library=gc |
|
STLport 版本 4 |
–compat=5 |
-library=stlport4 |
STLport 版本 4 调试 |
–compat=5 |
-library=stlport4_dbg |
Apache stdcxx 版本 4 |
-compat=5 |
-library=stdcxx4 |
CC 驱动程序在缺省情况下链接几个库的共享版本(包括 libc 和 libm),这通过为每个缺省库将 -llib 选项传递给链接程序来实现。(有关兼容模式和标准模式下的缺省库列表,请参见11.2.3 缺省 C++ 库。)
如果要静态链接其中任何缺省库,可以使用 -library 选项和 –staticlib 选项来静态链接 C++ 库。例如:
example% CC test.c -staticlib=Crun |
在此示例中,没有在命令中显式包括 -library 选项。这种情况下,无需 -library 选项,因为在标准模式(缺省模式)下,-library 的缺省设置是 Cstd,Crun。
也可以使用 -xnolib 编译器选项。使用 -xnolib 选项时,驱动程序不会将任何 -l 选项传递给 ld,所以您必须自己传递这些选项。以下示例显示了在 Solaris 8 或 Solaris 9 操作系统中如何静态链接 libCrun 以及如何动态链接 libm 和 libc:
example% CC test.c -xnolib -lCstd -Bstatic -lCrun– Bdynamic -lm -lc |
-l 选项的顺序很重要。– lCstd、– lCrun 和 -lm 选项位于 -lc 之前。
建议不要静态链接 libCrun 和 libCstd。而是生成 /usr/lib 中的动态版本以与其所安装在的 Solaris 版本一起使用。
有些 CC 选项链接到其他库。也可以使用 -xnolib 抑制这些库链接。例如,使用 -mt 选项会导致 CC 驱动程序将 -lthread 传递给 ld。但如果同时使用 –mt 和 –xnolib,CC 驱动程序不会将 -lthread 传递给 ld。有关更多信息,请参见A.2.153 –xnolib。有关 ld 的更多信息,请参见《链接程序和库指南》。
/lib 和 /usr/lib 中静态版本的 Solaris 库不再可用。例如,试图静态链接 libc 的操作将失败:
CC hello.cc -xnolib -lCrun -lCstd -Bstatic -lc |
C++ 编译器附带下列 C++ 运行时共享库:
libCCexcept.so.1(仅限 SPARC Solaris)
libcomplex.so.5(仅限 Solaris)
librwtool.so.2
libstlport.so.1
在 Linux 上,C++ 编译器附带这些附加库:
libCrun.so.1
libCstd.so.1
libdemangle.so
libiostream.so.1
在 Solaris 10 上,这些附加库以及其他一些库作为 Solaris C++ 运行时库软件包 SUNWlibC 的一部分安装。
如果应用程序使用 C++ 编译器附带的任何共享库,则 CC 驱动程序会安排运行路径(请参阅 -R 选项),该运行路径指向将在可执行文件中生成库的位置。如果之后将可执行文件部署到另一台计算机上,而该计算机上并没有在同一位置安装同一编译器版本,将找不到所需的共享库。
在程序启动时,可能根本找不到此库,或可能使用错误版本的库,从而导致错误的程序行为。在这种情况下,应该将所需库与可执行文件一起提供,并使用指向这些库将要安装的位置的运行路径进行生成。
《Using and Redistributing Solaris Studio Libraries in an Application》(《在应用程序中使用与重新分发 Solaris Studio 库》一文中详细讨论了该主题并提供了示例,请参见 http://developers.sun.com/sunstudio/documentation/techart/stdlibdistr.html
替换与编译器一起发布的标准库是有风险的,不能保证产生预期的结果。基本操作是禁用编译器提供的标准头文件和库,指定找到新的头文件和库(及库本身的名称)的目录。
编译器支持标准库的 STLport 和 Apache stdcxx 实现。有关更多信息,请参见12.3 STLport和12.4 Apache stdcxx 标准库。
可以替换大多数标准库及其关联头文件。替换的库是 libCstd,关联的头文件如下所示:
<algorithm> <bitset> <complex> <deque> <fstream <functional> <iomanip> <ios> <iosfwd> <iostream> <istream> <iterator> <limits> <list> <locale> <map> <memory> <numeric> <ostream> <queue> <set> <sstream> <stack> <stdexcept> <streambuf> <string> <strstream> <utility> <valarray> <vector>
库的可替换部分由一般称为 "STL" 的内容和字符串类、iostream 类及其帮助类组成。因为这些类和头文件是相互依赖的,所以不能仅替换其中的一部分。如果要替换任何一部分,就应该替换所有头文件和所有 libCstd。
标准头文件 <exception>、<new> 和 <typeinfo> 与编译器本身以及 libCrun 紧密相关,不能可靠替换。库 libCrun 包含了编译器依赖且不能替换的许多“帮助”函数。
从 C 继承的 17 个标准头文件(<stdlib.h>、<stdio.h>、<string.h> 等)与 Solaris 操作系统和 Solaris 基本运行时库 libc 紧密相关,不能可靠替换。这些头文件的 C++ 版本(<cstdlib>、<cstdio>、<cstring> 等)与基本 C 版本紧密相关,不能可靠替换。
要安装替换库,必须先确定替换头文件的位置和 libCstd 的替换库。为方便讨论,假定头文件放置在 /opt/mycstd/include 中,库放置在 /opt/mycstd/lib 中。假定库称为 libmyCstd.a。(库名最好以 "lib" 开头。)
每次编译时,都使用 -I 选项指向头文件的安装位置。此外,还使用 -library=no%Cstd 选项防止查找编译器自身版本的 libCstd 头文件。例如:
example% CC -I/opt/mycstd/include -library=no%Cstd... (compile) |
编译期间,-library=no%Cstd 选项防止搜索编译器自身版本的这些头文件所在的目录。
每次执行程序或库链接时,都使用 -library=no%Cstd 选项防止查找编译器自身的 libCstd,使用 -L 选项指向替换库所在的目录,以及使用 -l 选项指定替换库。示例:
example% CC -library=no%Cstd -L/opt/mycstd/lib -lmyCstd... (link) |
也可以直接使用库的完整路径名,而不使用 -L 和 -l 选项。例如:
example% CC -library=no%Cstd /opt/mycstd/lib/libmyCstd.a... (link) |
链接期间,-library=no%Cstd 选项防止链接编译器自身版本的 libCstd。
C 有 17 个标准头文件(<stdio.h>、<string.h>、<stdlib.h> 等)。这些头文件以 Solaris 操作系统的一部分提供,位于 /usr/include 目录中。C++ 也有这些头文件,但另外要求在全局名称空间和 std 名称空间中都有各种声明的名称。在 Solaris 操作系统版本 8 之前的版本中,C++ 编译器提供了自身版本的这些头文件而不是替换 /usr/include 目录中的头文件。
C++ 也有另一个版本的各个 C 标准头文件(<cstdio>、<cstring> 和 <cstdlib> 等),仅名称空间 std 中有各种声明的名称。最后,C++ 添加了 32 个自己的标准头文件(<string>、<utility>、<iostream> 等)。
标准头文件的明显实现将 C++ 源码中找到的名称用作包括的文本文件的名称。例如,标准头文件 <string>(或 <string.h>)可能指某目录中名为 string(或 string.h)的文件。这种明显实现有以下缺点:
如果具有名为 string 的目录或可执行程序,可能会错误地找到该目录或程序而不是标准头文件。
在 Solaris 8 操作系统之前的 Solaris 操作系统版本上,启用了 .KEEP_STATE 时,makefile 的缺省依赖性会导致尝试将标准头文件替换为可执行程序。(没有后缀的文件缺省假定为要生成的程序。)
为了解决这些问题,编译器 include 目录会包含一个与头文件同名的文件和一个指向它且具有唯一后缀 .SUNWCCh (SUNW 是所有编译器相关软件包的前缀,CC 指 C++ 编译器,h 是常用的头文件后缀)的符号链接。指定 <string> 后,编译器将其重写为 <string.SUNWCCh> 并搜索该名称。后缀名只能在编译器自己的 include 目录中找到。如果这样找到的文件是符号链接(正常情况下),编译器就对链接进行一次引用解除,并将结果(此例中是 string)用作错误消息和调试器引用的文件名。忽略文件的依赖性信息时,编译器使用带后缀的名称。
仅当出现在尖括号中且无需指定任何路径时,17 种标准 C 头文件和 32 种标准 C++ 头文件的两种格式才会发生名称重写。如果使用引号来代替尖括号指定任何路径组件或其他某些头文件,就不会有重写发生。
下表说明了通常的情况。
表 11–3 头文件搜索示例
源代码 |
编译器搜索 |
注释 |
---|---|---|
<string> |
string.SUNWCCh |
C++ 字符串模板 |
<cstring> |
cstring.SUNWCCh |
C string.h 的 C++ 版本 |
<string.h> |
string.h.SUNWCCh |
C string.h |
<fcntl.h> |
fcntl.h |
不是标准 C 或 C++ 头文件 |
"string" |
string |
双引号,不是尖括号 |
<../string> |
../string |
指定的路径 |
如果编译器未找到 header.SUNWCCh,则编译器将重新搜索 #include 指令中提供的名称。例如,如果使用指令 #include <string>,编译器就尝试查找名为 string.SUNWCCh 的文件。如果搜索失败,编译器就查找名为 string 的文件。
由于11.7.5 标准头文件实现中介绍的搜索算法,您无需提供11.7.3 安装替换库中介绍的 SUNWCCh 版本的替换头文件。但是会遇到某些上文所述的问题。如果这样,建议为每个无后缀的头文件添加后缀为 .SUNWCCh 的符号链接。也就是说,对于文件 utility,可以运行命令
example% ln -s utility utility.SUNWCCh |
编译器第一次查找 utility.SUNWCCh 时,会找到它,而不会和其他名为 utility 的文件或目录混淆。
不支持替换标准 C 头文件。如果仍然希望提供标准头文件的自己的版本,那么建议按以下步骤操作:
将所有替换头文件放置在一个目录中。
在该目录中创建指向每个替换头文件的 .SUNWCCh 符号链接。
在每次调用编译器时使用 -I 指令,搜索包含替换头文件的目录。
例如,假设有 <stdio.h> 和 <cstdio> 的替换。请将文件 stdio.h 和 cstdio 放在目录 /myproject/myhdr 中。在该目录中,运行如下命令:
example% ln -s stdio.h stdio.h.SUNWCCh example% ln -s cstdio cstdio.SUNWCCh |
每次编译时使用 -I/myproject/mydir 选项。
如果要替换任何 C 头文件,就必须成对替换。例如,如果替换 <time.h>,还应该替换 <ctime>。
替换头文件必须与被替换版本具有相同的效果。也就是说,各种运行时库(如 libCrun、libC、libCstd、libc 和 librwtool)是使用标准头文件中的定义生成的。如果替换文件不匹配,那么程序不能工作。
当在缺省(标准)模式下编译时,编译器可以访问 C++ 标准指定的整个库。库组件包括一般称为标准模板库 (Standard Template Library, STL) 的库和下列组件。
字符串类
数字类
标准版本的流 I/O 类
基本内存分配
异常类
运行时类型信息
术语 STL 没有正式的定义,但是通常理解为包括容器、迭代器以及算法。以下标准库头的子集可以认为包含了 STL。
<algorithm>
<deque>
<iterator>
<list>
<map>
<memory>
<queue>
<set>
<stack>
<utility>
<vector>
C++ 标准库 (libCstd) 基于 RogueWave Standard C++ Library, Version 2。该库仅在编译器的缺省模式 (-compat=5) 下可用,并且在使用 -compat[=4] 选项时不支持该库。
C++ 编译器还支持 STLport 的标准库实现版本 4.5.3。libCstd 仍是缺省库,STLport 的产品只是备选的。有关更多信息,请参见12.3 STLport。
如果需要使用自己的 C++ 标准库版本而非编译器附带的某一版本,可以通过指定 -library=no%Cstd 选项来实现。替换与编译器一起发布的标准库是有风险的,不能保证产生预期的结果。有关更多信息,请参见11.7 替换 C++ 标准库。
有关标准库的详细信息,请参见《标准 C++ 库用户指南》和《Standard C++ Class Library Reference》。
表 12–1 列出了完整标准库的头文件以及每个头文件的简要介绍。
表 12–1 C++ 标准库头文件
头文件 |
说明 |
---|---|
<algorithm> |
操作容器的标准算法 |
<bitset> |
位的固定大小序列 |
<complex> |
数字类型表示复数 |
<deque> |
支持在端点增加和删除的序列 |
<exception> |
预定义异常类 |
<fstream> |
文件的流 I/O |
<functional> |
函数对象 |
<iomanip> |
iostream 操纵符 |
<ios> |
iostream 基类 |
<iosfwd> |
iostream 类的前向声明 |
<iostream> |
基本流 I/O 功能 |
<istream> |
输入 I/O 流 |
<iterator> |
遍历序列的类 |
<limits> |
数字类型的属性 |
<list> |
排序的序列 |
<locale> |
国际化支持 |
<map> |
带有键/值对的关联容器 |
<memory> |
专用内存分配器 |
<new> |
基本内存分配和释放 |
<numeric> |
通用的数字操作 |
<ostream> |
输出 I/O 流 |
<queue> |
支持在头部增加和在尾部删除的序列 |
<set> |
有唯一键值的关联容器 |
<sstream> |
将内存中的字符串用为源或接收器的流 I/O |
<stack> |
支持在头部增加和删除的序列 |
<stdexcept> |
附加的标准异常类 |
<streambuf> |
iostream 的缓冲区类 |
<string> |
字符序列 |
<typeinfo> |
运行时类型标识 |
<utility> |
比较运算符 |
<valarray> |
用于数字编程的值数组 |
<vector> |
支持随机访问的序列 |
表 12–2 列出了标准库中每个组件的可用文档。
表 12–2 C++ 标准库手册页
手册页 |
概述 |
---|---|
Algorithms |
在容器和序列上执行各种操作的通用算法 |
Associative_Containers |
排序的容器 |
Bidirectional_Iterators |
可以读取和写入,并且可以双向遍历容器的迭代器 |
Containers |
标准模板库 (standard template library, STL) 集合 |
Forward_Iterators |
可以读取和写入的前移式迭代器 |
Function_Objects |
定义了 operator() 的对象 |
Heap_Operations |
请参见与 make_heap、pop_heap、push_heap 和 sort_heap 对应的条目 |
Input_Iterators |
只读的前移式迭代器 |
Insert_Iterators |
允许迭代器向容器插入元素而非覆盖容器内元素的迭代器适配器 |
Iterators |
集合遍历和修改的指针泛化 |
Negators |
用于遍历谓词函数对象检测的函数适配器和函数对象 |
Operators |
C++ 标准模板库输出的运算符 |
Output_Iterators |
只写的前移式迭代器 |
Predicates |
返回布尔 (true/false) 值或整型值的函数或函数对象 |
Random_Access_Iterators |
可以读取、写入并随机访问容器的迭代器 |
Sequences |
组织序列集合的容器 |
Stream_Iterators |
包括允许直接在流上使用通用算法的 ostream 和 istream 的迭代器功能 |
__distance_type |
确定迭代程序所用距离的类型(已过时) |
__iterator_category |
确定迭代程序所属的类别(已过时) |
__reverse_bi_iterator |
向后遍历集合的迭代器 |
accumulate |
将一定范围内的所有元素聚集到单个值中 |
adjacent_difference |
输出在一定范围内每个相邻元素对之间的差别的序列 |
adjacent_find |
寻找在序列中相等的第一个相邻元素对 |
advance |
按特定的距离将迭代器向前或者向后移动(如可能) |
allocator |
在标准库容器中用于存储管理的缺省分配器对象 |
auto_ptr |
一个简单、智能的指针类 |
back_insert_iterator |
用于在集合末端插入项目的插入迭代器 |
back_inserter |
用于在集合末端插入项目的插入迭代器 |
basic_filebuf |
将输入序列或输出序列与文件关联的类 |
basic_fstream |
支持对命名文件的读取和写入,或者与文件描述符关联的设备的读取和写入 |
basic_ifstream |
支持从命名文件读取或者从其他与文件描述符关联的设备读取 |
basic_ios |
一个包含所有流都需要的通用函数的基类 |
basic_iostream |
帮助格式化或者翻译由流缓冲区控制的字符序列 |
basic_istream |
帮助读取或者翻译由流缓冲区控制的序列输入 |
basic_istringstream |
支持从内存中的数组读取 basic_string<charT,traits,Allocator> 类的对象 |
basic_ofstream |
支持写入命名文件或者其他与文件描述符关联的设备 |
basic_ostream |
帮助格式化或者写入由流缓冲区控制的序列输出 |
basic_ostringstream |
支持写入 basic_string<charT,traits,Allocator> 类的对象 |
basic_streambuf |
用于派生便于字符序列控制的各种流缓冲区的抽象基类 |
basic_string |
处理类似字符实体的模板化类 |
basic_stringbuf |
将输入或者输出序列与任意字符序列关联 |
basic_stringstream |
支持在内存中的数组中写入和读取 basic_string<charT,traits,Allocator> 类的对象 |
binary_function |
创建二元函数对象的基类 |
binary_negate |
返回二元谓词结果补码的函数对象 |
binary_search |
对容器中的值执行二元搜索 |
bind1st |
用于将值绑定到函数对象的模板化实用程序 |
bind2nd |
用于将值绑定到函数对象的模板化实用程序 |
binder1st |
用于将值绑定到函数对象的模板化实用程序 |
binder2nd |
用于将值绑定到函数对象的模板化实用程序 |
bitset |
用于存储和处理固定大小位序列的模板类和相关函数 |
cerr |
控制向与 <cstdio> 中声明的对象 stderr 关联的无缓冲流缓冲区的输出 |
char_traits |
具有用于 basic_string 容器和 iostream 类的类型和运算的特性类 |
cin |
控制从与 <cstdio> 中声明的对象 stdin 关联的流缓冲区的输入 |
clog |
控制向与 <cstdio> 中声明的对象 stderr 关联的流缓冲区的输出 |
codecvt |
代码转换侧面 |
codecvt_byname |
一个包含以命名语言环境为基础的代码集转换分类工具的侧面 |
collate |
一个字符串检验、比较和散列侧面 |
collate_byname |
一个字符串检验、比较和散列侧面 |
compare |
返回真或假的二元函数或函数对象 |
complex |
C++ 复数库 |
copy |
复制一定范围内的元素 |
copy_backward |
复制一定范围内的元素 |
count |
计算容器中满足给定条件的元素的数量 |
count_if |
计算容器中满足给定条件的元素的数量 |
cout |
控制向与 <cstdio> 中声明的对象 stdout 关联的流缓冲区的输出 |
ctype |
包括字符分类工具的侧面 |
ctype_byname |
一个包含以命名语言环境为基础的字符分类工具的侧面 |
deque |
一个支持随机访问迭代器并支持在开始和结束位置进行高效插入/删除的序列 |
distance |
计算两个迭代器之间的距离 |
divides |
返回用第一个参数除以第二个参数所得到的结果 |
equal |
比较等式的两个范围 |
equal_range |
在集合中找到最大的子范围,可在该范围中插入一个给定值而无需违反集合排序 |
equal_to |
如果第一个参数与第二个参数相等就返回真的二元函数对象 |
exception |
一个支持逻辑和运行时错误的类 |
facets |
用于封装语言环境功能分类的类系列 |
filebuf |
将输入序列或输出序列与文件关联的类 |
fill |
用给定值初始化一个范围 |
fill_n |
用给定值初始化一个范围 |
find |
在序列中寻找出现的值 |
find_end |
在序列中寻找上次出现的子序列 |
find_first_of |
在序列中寻找在另一个序列中第一次出现的值 |
find_if |
在满足特定谓词的序列中寻找第一次出现的值 |
for_each |
将函数应用于范围内的每个元素 |
fpos |
保持 iostream 类的位置信息 |
front_insert_iterator |
用于在集合起始端插入条目的插入迭代器 |
front_inserter |
用于在集合起始端插入条目的插入迭代器 |
fstream |
支持对命名文件的读取和写入,或者与文件描述符关联的设备的读取和写入 |
generate |
初始化一个具有由值产生器类产生的值的容器 |
generate_n |
初始化一个具有由值产生器类产生的值的容器 |
get_temporary_buffer |
基于指针的基元,用于处理内存 |
greater |
如果第一个参数比第二个参数大就返回真的二元函数对象 |
greater_equal |
如果第一个参数大于或等于第二个参数就返回真的二元函数对象 |
gslice |
用于表示数组的通用片的数字数组类 |
gslice_array |
用于表示 valarray 的类 BLAS 片的数字数组类 |
has_facet |
用于确定语言环境是否具有给定侧面的函数模板 |
ifstream |
支持从命名文件读取或者从其他与文件描述符关联的设备读取 |
includes |
已排序序列的一系列基本操作 |
indirect_array |
用于表示从 valarray 中所选元素的数字数组类 |
inner_product |
计算两个范围 A 和 B 的内积 A X B |
inplace_merge |
将两个已排序的序列合并成为一个 |
insert_iterator |
用于将项目插入集合而非覆盖集合的插入迭代器 |
inserter |
用于将项目插入集合而非覆盖集合的插入迭代器 |
ios |
一个包含所有流都需要的通用函数的基类 |
ios_base |
定义成员类型并维护从它继承的类的数据 |
iosfwd |
声明输入/输出库模板类并使之专用于宽字符和微型字符 |
isalnum |
确定字符是字母还是数字 |
isalpha |
确定字符是否为字母 |
iscntrl |
确定字符是否为控制字符 |
isdigit |
确定字符是否为十进制数字 |
isgraph |
确定字符是否为图形字符 |
islower |
确定字符是否为小写形式 |
isprint |
确定字符是否可输出 |
ispunct |
确定字符是否为标点符号 |
isspace |
确定字符是否为空格 |
istream |
帮助读取或者翻译由流缓冲区控制的序列输入 |
istream_iterator |
具有 istreams 迭代器功能的流迭代器 |
istreambuf_iterator |
从流缓冲区读取为其构造的连续字符 |
istringstream |
支持从内存中的数组读取 basic_string<charT,traits,Alocator> 类的对象 |
istrstream |
从内存中的数组读取字符 |
isupper |
确定字符是否为大写形式 |
isxdigit |
确定字符是否为十六进制数字 |
iter_swap |
交换两个位置的值 |
iterator |
基本的迭代器类 |
iterator_traits |
返回有关迭代器的基本信息 |
less |
如果第一个参数比第二个参数小就返回真的二元函数对象 |
less_equal |
如果第一个参数小于或等于第二个参数就返回真的二元函数对象 |
lexicographical_compare |
按照字典编排顺序来比较两个范围 |
limits |
请参阅 numeric_limits |
list |
支持双向迭代器的序列 |
locale |
包含多态侧面集的本地化类 |
logical_and |
如果两个参数都为真就返回真的二元函数对象 |
logical_not |
如果参数是假就返回真的一元函数对象 |
logical_or |
如果两个参数有一个为真就返回真的二元函数 |
lower_bound |
确定在已排序容器中元素的第一个有效位置 |
make_heap |
创建堆 |
map |
用唯一关键字访问非关键字值的关联容器 |
mask_array |
给出了 valarray 的屏蔽视图的数字数组类 |
max |
查找并返回一对值中的最大值 |
max_element |
查找一个范围中的最大值 |
mem_fun |
与指向成员函数的指针相匹配的函数对象,替代全局函数 |
mem_fun1 |
与指向成员函数的指针相匹配的函数对象,替代全局函数 |
mem_fun_ref |
与指向成员函数的指针相匹配的函数对象,替代全局函数 |
mem_fun_ref1 |
与指向成员函数的指针相匹配的函数对象,替代全局函数 |
merge |
将两个已排序的序列合并为第三个序列 |
messages |
消息传送侧面 |
messages_byname |
消息传送侧面 |
min |
查找并返回一对值中的最小值 |
min_element |
查找一个范围中的最小值 |
minus |
返回用第一个参数减去第二个参数所得到的结果 |
mismatch |
比较来自两个序列的元素并返回前两个不匹配的元素 |
modulus |
返回第一个参数除以第二个参数所得到的余数 |
money_get |
输入的货币格式 |
money_put |
输出的货币格式 |
moneypunct |
货币标点格式 |
moneypunct_byname |
货币标点格式 |
multimap |
用关键字访问非关键字值的关联容器 |
multiplies |
用于返回第一个参数与第二个参数相乘结果的二元函数对象。 |
multiset |
允许快速访问已保存关键字值的关联容器 |
negate |
返回其参数负值的一元函数对象 |
next_permutation |
生成以排序函数为基础的序列的连续置换 |
not1 |
对一元谓词函数对象进行求反操作的函数适配器 |
not2 |
对一元谓词函数对象进行求反操作的函数适配器 |
not_equal_to |
如果第一个参数与第二个参数不相等就返回 true 的二元函数对象 |
nth_element |
重新排列集合,以使按照排序顺序,低于第 n 个元素的所有元素位于该元素之前,高于第 n 个元素的所有元素位于该元素之后 |
num_get |
输入的数字格式 |
num_put |
输出的数字格式 |
numeric_limits |
表示标量类型信息的类 |
numpunct |
数字标点格式 |
numpunct_byname |
数字标点格式 |
ofstream |
支持写入命名文件或者其他与文件描述符关联的设备 |
ostream |
帮助格式化或者写入由流缓冲区控制的序列输出 |
ostream_iterator |
流迭代器允许使用具有 ostream 和 istream 的迭代器 |
ostreambuf_iterator |
向从其构造的流缓冲区对象写入连续的字符 |
ostringstream |
支持写入 basic_string<charT,traits,Allocator> 类的对象 |
ostrstream |
写入一个在内存中的数组 |
pair |
异类值对的模板 |
partial_sort |
对实体集合排序的模板化算法 |
partial_sort_copy |
对实体集合排序的模板化算法 |
partial_sum |
计算一组值的连续部分的和 |
partition |
将所有满足给定谓词的实体放置在不满足给定谓词的实体后面 |
permutation |
生成以排序函数为基础的序列的连续置换 |
plus |
用于返回第一个参数与第二个参数相加结果的二元函数对象 |
pointer_to_binary_function |
采用指向二元函数的指针的函数对象,替代 binary_function |
pointer_to_unary_function |
采用指向函数的指针的函数对象类,替代 unary_function |
pop_heap |
从堆中移出最大的元素 |
prev_permutation |
生成以排序函数为基础的序列的连续置换 |
priority_queue |
像优先队列一样运行的容器适配器 |
ptr_fun |
一个与指向某函数的指针对应的过载函数,替换一个函数 |
push_heap |
将一个新元素放入堆 |
queue |
像队列一样运行的容器适配器(先入先出) |
random_shuffle |
集合的随机混洗元素 |
raw_storage_iterator |
使基于迭代器的算法能够将结果存入尚未初始化的内存中 |
remove |
将所需元素移动到容器的前端,并返回一个说明所需元素序列的结束位置的迭代器 |
remove_copy |
将所需元素移动到容器的前端,并返回一个说明所需元素序列的结束位置的迭代器 |
remove_copy_if |
将所需元素移动到容器的前端,并返回一个说明所需元素序列的结束位置的迭代器 |
remove_if |
将所需元素移动到容器的前端,并返回一个说明所需元素序列的结束位置的迭代器 |
replace |
用新值替换集合中的元素 |
replace_copy |
用新值替换集合中的元素,并将修改过的序列移入结果 |
replace_copy_if |
用新值替换集合中的元素,并将修改过的序列移入结果 |
replace_if |
用新值替换集合中的元素 |
return_temporary_buffer |
基于指针的基元,用于处理内存 |
reverse |
反转集合中元素的顺序 |
reverse_copy |
将集合中元素复制到新集合时反转它们的顺序 |
reverse_iterator |
向后遍历集合的迭代器 |
rotate |
将包含第一个元素到第中间减 1 个元素的部分与包含从中间到最后元素的部分交换 |
rotate_copy |
将包含第一个元素到第中间减 1 个元素的部分与包含从中间到最后元素的部分交换 |
search |
在值(这些值在元素状态时与标明范围内的值相等)的序列中查找子序列 |
search_n |
在值(这些值在元素状态时与标明范围内的值相等)的序列中查找子序列 |
set |
支持唯一关键字的关联容器 |
set_difference |
构建已排序差集的基本设置操作 |
set_intersection |
构建已排序交集的基本设置操作 |
set_symmetric_difference |
构建已排序对称差集的基本设置操作 |
set_union |
构建已排序并集的基本设置操作 |
slice |
表示数组的类 BLAS 片的数字数组类 |
slice_array |
用于表示 valarray 的类 BLAS 片的数字数组类 |
smanip |
用于实现参数化操纵符的帮助程序类 |
smanip_fill |
用于实现参数化操纵符的帮助程序类 |
sort |
对实体集合排序的模板化算法 |
sort_heap |
将堆转换为已排序的集合 |
stable_partition |
在保持每组中元素的相对顺序的同时,将所有满足给定谓词的实体放在所有不满足给定谓词的实体之前 |
stable_sort |
对实体集合排序的模板化算法 |
stack(块) |
像堆栈一样运行的容器适配器(后入先出) |
streambuf |
用于派生便于字符序列控制的各种流缓冲区的抽象基类 |
string |
basic_string<char, char_traits<char>, allocator<char>> 的 typedef |
stringbuf |
将输入或者输出序列与任意字符序列关联 |
stringstream |
支持在内存中的数组中写入和读取 basic_string<charT,traits,Alocator> 类的对象 |
strstream |
在内存中读取或者写入一个数组 |
strstreambuf |
将输入序列或者输出序列与微型字符数组(其元素存储任意值)关联 |
swap |
交换值 |
swap_ranges |
将一个位置的值与在其他位置的值交换 |
time_get |
输入的时间格式 |
time_get_byname |
输入的时间格式,以命名语言环境为基础 |
time_put |
输出的时间格式 |
time_put_byname |
输出的时间格式,以命名语言环境为基础 |
tolower |
将字符转换为小写形式 |
toupper |
将字符转换为大写形式 |
transform |
将操作应用到集合中的一系列值并且存储结果 |
unary_function |
创建一元函数对象的基类 |
unary_negate |
返回一元谓词结果补码的函数对象 |
uninitialized_copy |
使用构造从一个范围向另一个位置复制值的算法 |
uninitialized_fill |
使用了在集合中设置值的构造算法的算法 |
uninitialized_fill_n |
使用了在集合中设置值的构造算法的算法 |
unique |
从一个值范围移除连续的重复值并将得到的唯一值放入结果 |
unique_copy |
从一个值范围移除连续的重复值并将得到的唯一值放入结果 |
upper_bound |
确定已排序容器中值的最后一个有效位置 |
use_facet |
用于获取侧面的模板函数 |
valarray |
用于数字操作的优化数组类 |
vector |
支持随机访问迭代器的序列 |
wcerr |
控制向与 <cstdio> 中声明的对象 stderr 关联的无缓冲流缓冲区的输出 |
wcin |
控制从与 <cstdio> 中声明的对象 stdin 关联的流缓冲区的输入 |
wclog |
控制向与 <cstdio> 中声明的对象 stderr 关联的流缓冲区的输出 |
wcout |
控制向与 <cstdio> 中声明的对象 stdout 关联的流缓冲区的输出 |
wfilebuf |
将输入序列或输出序列与文件关联的类 |
wfstream |
支持对命名文件的读取和写入,或者与文件描述符关联的设备的读取和写入 |
wifstream |
支持从命名文件读取或者从其他与文件描述符关联的设备读取 |
wios |
一个包含所有流都需要的通用函数的基类 |
wistream |
帮助读取或者翻译由流缓冲区控制的序列输入 |
wistringstream |
支持从内存中的数组读取 basic_string<charT,traits,Allocator> 类的对象 |
wofstream |
支持写入命名文件或者其他与文件描述符关联的设备 |
wostream |
帮助格式化或者写入由流缓冲区控制的序列输出 |
wostringstream |
支持写入 basic_string<charT,traits,Allocator> 类的对象 |
wstreambuf |
用于派生便于字符序列控制的各种流缓冲区的抽象基类 |
wstring |
basic_string<wchar_t, char_traits<wchar_t>, allocator<wchar_t>> 的 typedef |
wstringbuf |
将输入或者输出序列与任意字符序列关联 |
如果要使用替换 libCstd 的标准库,请使用标准库的 STLport 实现。可以使用以下编译器选项关闭 libCstd 并改用 STLport 库:
-library=stlport4
有关更多信息,请参见A.2.49 -library=l[ ,l...]。
本发行版包括称为 libstlport.a 的静态归档文件和称为 libstlport.so 的动态库。
决定是否使用 STLport 实现之前,请先考虑以下信息:
STLport 是开放源代码产品,并不能保证不同发行版本之间的兼容性。也就是说,使用后续版本的 STLport 编译,可能导致使用 STLport 4.5.3 编译的应用程序中断。使用后续版本的 STLport 编译的二进制文件可能无法与使用 STLport 4.5.3 编译的二进制文件链接。
stlport4、Cstd 和 iostream 库都提供了自己的 I/O 流实现。如果使用 -library 选项指定其中多个库,会导致出现不确定的程序行为。
编译器的后续发行版本可能不包括 STLport4,只包括更新版本的 STLport。在将来发行版中可能不能使用编译器选项 -library=stlport4,但可能会用引用更高 STLport 版本的选项替换该选项。
Tools.h++ 不支持 STLport。
STLport 与缺省的 libCstd 是二进制不兼容的。如果使用标准库的 STLport 实现,则必须使用选项 -library=stlport4 编译和链接包括第三方库在内的所有文件。这意味着存在一些限制,例如,不可同时使用 STLport 实现和 C++ 区间数学库 libCsunimath。这是因为对 libCsunimath 编译时使用的是缺省的库头文件而不是 STLport。
如果决定使用 STLport 实现,那么一定要包括代码隐式引用的头文件。允许标准头文件,但不必要包括另一个标准头文件作为实现的一部分。
如果使用 -compat=4 编译,则不能使用 STLport 实现。
请参见分发自述文件,了解可依照“最终用户目标代码许可协议”的条款随您的可执行文件或库重新分发的库和对象文件列表。此自述文件的 C++ 部分列出该编译器发行版支持的 STLport.so 版本。此自述文件位于此发行版的 Oracle Solaris Studio 软件的法律页面上,网址为 http://www.oracle.com/technetwork/server-storage/solarisstudio/overview/index.html
因为以下测试示例中的代码将库实现假定为不可移植,所以在该测试示例中不能使用 STLport 编译。具体来说,它假定 <vector> 或 <iostream> 自动包含 <iterator>,这是无效假定。
#include <vector> #include <iostream> using namespace std; int main () { vector <int> v1 (10); vector <int> v3 (v1.size()); for (int i = 0; i < v1.size (); i++) {v1[i] = i; v3[i] = i;} vector <int> v2(v1.size ()); copy_backward (v1.begin (), v1.end (), v2.end ()); ostream_iterator<int> iter (cout, " "); copy (v2.begin (), v2.end (), iter); cout << endl; return 0; } |
要解决该问题,请将 <iterator> 包含在源代码中。
通过使用 -library=stdcxx4 进行编译,在 Solaris 中使用 Apache stdcxx 版本 4 C++ 标准库代替缺省的 libCstd。此选项还会隐式设置 -mt 选项。stdcxx 库需要使用多线程模式。必须在整个应用程序的每个编译和链接命令中一致使用此选项。用 -library=stdcxx4 编译的代码与用缺省的 -library=Cstd 或可选的 -library=stlport4 编译的代码不能用在同一程序中。
与 C 类似,C++ 没有内建输入或输出语句。相反,I/O 工具是由库提供的。C++ 编译器提供了 iostream 类的传统实现和 ISO 标准实现。
在标准模式(缺省模式)下,传统 iostream 类包含在 libiostream 中。如果源代码使用传统 iostream 类,且要在标准模式下编译源代码,请使用 libiostream。如果要在标准模式下使用传统 iostream 功能,请将 iostream.h 头文件包括进来并使用 -library=iostream 选项进行编译。
标准 iostream 类只能用于标准模式下,且包含在 C++ 标准库 libCstd 中。
本章介绍了传统 iostream 库并提供了其使用示例,但并未完整介绍 iostream 库。有关更多详细信息,请参见 iostream 库手册页。要访问传统 iostream 手册页,请键入:man -s 3CC4name
有四个预定义的 iostream:
除了 cerr 之外,所有预定义的 iostream 都是完全缓冲的。请参见13.3.1 使用 iostream 进行输出和13.3.2 使用 iostream 进行输入。
通过将 iostream 库包括进来,程序可以使用许多输入流或输出流。每个流都具有某些源或接收器,如下所示:
标准输入
标准输出
标准错误
文件
字符数组
流可以被限定到输入或输出,或同时具有输入和输出。iostream 库使用两个处理层来实现这些流。
较低层实现了序列,即字符的简单流。这些序列由 streambuf 类或从其派生的类实现。
较高层对序列执行格式化操作。这些格式化操作由 istream 和 ostream 类实现,这两个类将从 streambuf 类派生的类型的对象作为成员。附加类 iostream 用于执行输入和输出的流。
标准输入、输出和错误由从类 istream 或 ostream 派生的特殊类对象处理。
分别从 istream、ostream 和 iostream 派生的 ifstream、ofstream 和 fstream 类用于处理文件的输入和输出。
分别从 istream、ostream 和 iostream 派生的 istrstream、ostrstream 和 strstream 类用于处理字符数组的输入和输出。
打开输入或输出流时,要创建其中一种类型的对象,并将流的 streambuf 成员与设备或文件关联。通常通过流构造函数执行此关联,因此不用直接使用 streambuf。iostream 库为标准输入、标准输出和错误输出预定义了流对象,因此不必为这些流创建自己的对象。
可以使用运算符或 iostream 成员函数将数据插入流(输出)或从流(输入)提取数据,以及控制插入或提取的数据的格式。
如果要插入和提取新的数据类型(其中一个类),通常需要重载插入和提取运算符。
要使用来自传统 iostream 库的例程,必须针对所需的库部分将头文件包括进来。下表对头文件进行了具体描述。
表 13–1 iostream 例程头文件
头文件 |
说明 |
---|---|
声明 iostream 库的基本功能。 |
|
声明文件专用的 iostream 和 streambuf。包括了 iostream.h。 |
|
声明字符数组专用的 iostream 和 streambuf。包括了 iostream.h。 |
|
(已过时)声明 stdio 文件专用的 iostream 和 streambuf。包括了 iostream.h。 |
|
(已过时)包括了 iostream.h、fstream.h、iomanip.h 和 stdiostream.h。用于兼容 C++ 1.2 版的旧式流。 |
通常程序中不需要所有这些头文件,而仅包括所需声明的头文件。在兼容模式 (-compat[=4]) 下,传统 iostream 库是 libC 的一部分,它由 CC 驱动程序自动链接。在标准模式(缺省模式)下, libiostream 包含传统 iostream 库。
使用 iostream 进行的输出通常依赖于重载的左移运算符 (<<)(在 iostream 上下文中,称为插入运算符)。要将值输出到标准输出,应将值插入预定义的输出流 cout 中。例如,要将给定值 someValue 发送到标准输出,可以使用以下语句:
cout << someValue; |
对于所有内置类型,都会重载插入运算符,且 someValue 表示的值转换为其适当的输出表示形式。例如,如果 someValue 是 float 值,<< 运算符会将该值转换为带小数点的适当数字序列。此处是将 float 值插入输出流中,因此 << 称为浮点插入器。通常,如果给定类型 X,<< 称为 X 插入器。ios(3CC4) 手册页中讨论了输出格式以及如何控制输出格式。
iostream 库不支持用户定义的类型。如果要定义以自己的方式输出的类型,必须定义一个插入器(即,重载 << 运算符)来正确处理它们。
<< 可以多次应用。要将两个值插入 cout 中,可以使用类似于以下示例中的语句:
cout << someValue << anotherValue; |
以上示例的输出在两个值间不显示空格。因此您可能会要按以下方式编写编码:
cout << someValue << " " << anotherValue; |
运算符 << 优先作为左移运算符(其内置含义)使用。与其他运算符一样,总是可以使用圆括号来指定操作顺序。使用括号来避免优先级问题是个很好的方法。下列四个语句中,前两个是等价的,但后两个不是。
cout << a+b; // + has higher precedence than << cout << (a+b); cout << (a&y); // << has precedence higher than & cout << a&y; // probably an error: (cout << a) & y |
以下示例定义了 string 类:
#include <stdlib.h> #include <iostream.h> class string { private: char* data; size_t size; public: // (functions not relevant here) friend ostream& operator<<(ostream&, const string&); friend istream& operator>>(istream&, string&); }; |
在此示例中,必须将插入运算符和提取运算符定义为友元,因为 string 类的数据部分是 private。
ostream& operator<< (ostream& ostr, const string& output) { return ostr << output.data;} |
以下是为要用于 string 而重载的 operator<< 的定义。
cout << string1 << string2; |
运算符 << 将 ostream&(也就是对 ostream 的引用)作为其第一个参数,并返回相同的 ostream,这样就可以在一个语句中合并多个插入。
通常情况下,重载 operator<< 时不必检查错误,因为有 iostream 库传播错误。
出现错误时,所在的 iostream 会进入错误状态。iostream 状态中的各个位根据错误的一般类别进行设置。iostream 中定义的插入器不会尝试将数据插入处于错误状态的任何流,因此这种尝试不会更改 iostream 的状态。
通常,处理错误的推荐方法是定期检查某些中心位置中输出流的状态。如果有错误,就应该以某些方式来处理。本章假定您定义了函数 error,该函数可采用字符串并中止程序。error 不是预定义的函数。有关 error 函数的示例,请参见13.3.9 处理输入错误。可以使用运算符 ! 检查 iostream 的状态,如果 iostream 处于错误状态,该运算符会返回非零值。例如:
if (!cout) error("output error"); |
还有另外一种方法来测试错误。ios 类可定义 operator void *(),它在有错误时返回空指针。您可以使用如下语句:
if (cout << x) return; // return if successful |
也可以使用函数 good,它是 ios 的成员:
if (cout.good()) return; // return if successful |
enum io_state {goodbit=0, eofbit=1, failbit=2, badbit=4, hardfail=0x80}; |
有关错误函数的详细信息,请参见 iostream 手册页。
与大多数 I/O 库一样,iostream 通常会累积输出并将其发送到较大且效率通常较高的块中。如果要刷新缓冲区,只要插入特殊值 flush。例如:
cout << "This needs to get out immediately." << flush; |
flush 是一种称为操纵符的对象示例,它是一个值,可以插入 iostream 中以起到一定作用,而不是使输出其值。它实际上是一个函数,采用 ostream& 或 istream& 参数,在对其执行某些操作后返回其参数(请参见13.7 操纵符)。
要获得原始二进制形式的值输出,请使用以下示例所示的成员函数 write。该示例显示了原始二进制形式的 x 输出。
cout.write((char*)&x, sizeof(x)); |
以上示例将 &x 转换为 char*,这违反了类型规程。一般情况下,这样做无关大碍,但如果 x 的类型是具有指针、虚拟成员函数的类或是需要 nontrivial 构造函数操作的类,就无法正确读回以上示例写入的值。
使用 iostream 进行的输入类似于输出。需要使用提取运算符 >>,可以像插入操作那样将提取操作串接在一起。例如:
cin >> a >> b; |
该语句从标准输入获得两个值。与其他重载运算符一样,所用的提取器取决于 a 和 b 的类型(如果 a 和 b 的类型不同,则使用两个不同的提取器)。ios(3CC4) 手册页中详细讨论了输入格式以及如何控制输入格式。通常,前导空白字符(空格、换行符、标签、换页等)被忽略。
要输入新的类型时,如同重载输出的插入运算符,请重载输入的提取运算符。
类 string 定义了其提取运算符,如以下代码示例所示:
istream& operator>> (istream& istr, string& input) { const int maxline = 256; char holder[maxline]; istr.get(holder, maxline, ”\n’); input = holder; return istr; } |
get 函数从输入流 istr 读取字符,并将其存储在 holder 中,直到读取了 maxline-1 字符、遇到新行或 EOF(无论先发生哪一项)。然后,holder 中的数据以空终止。最后,holder 中的字符复制到目标字符串。
按照约定,提取器转换其第一个参数中的字符(此示例中是 istream& istr),将其存储在第二个参数(始终是引用),然后返回第一个参数。因为提取器会将输入值存储在第二个参数中,所以第二个参数必须是引用。
此处提及这个预定义的提取器是因为它可能产生问题。使用方法如下:
char x[50]; cin >> x; |
该提取器跳过前导空白,提取字符并将其复制到 x 中,直至遇到另一个空白字符。最后完成具有终止空 (0) 字符的字符串。因为输入会溢出给定的数组,所以要小心操作。
您还必须确保指针指向了分配的存储。例如,下面列出了一个常见的错误:
char * p; // not initialized cin >> p; |
因为没有告知存储输入数据的位置,所以会导致程序的终止。
除了使用 char 提取器外,还可以使用任一形式的 get 成员函数获取一个字符。例如:
char c; cin.get(c); // leaves c unchanged if input fails int b; b = cin.get(); // sets b to EOF if input fails |
以下方法可以只跳过空格,并在制表符、换行符或任何其他字符处停止:
int a; do { a = cin.get(); } while(a ==’ ’); |
如果需要读取二进制值(如使用成员函数 write 写入的值),可以使用 read 成员函数。以下示例说明了如何使用 read 成员函数输入原始二进制形式的 x,这是先前使用 write 的示例的反向操作。
cin.read((char*)&x, sizeof(x)); |
可以使用 peek 成员函数查看流中的下一个字符,而不必提取该字符。例如:
if (cin.peek()!= c) return 0; |
缺省情况下,iostream 提取器会跳过前导空白。可以关闭 跳过标志防止这种情况发生。以下示例先关闭了 cin 跳过空白功能,然后将其打开:
cin.unsetf(ios::skipws); // turn off whitespace skipping ... cin.setf(ios::skipws); // turn it on again |
可以使用 iostream 操纵符 ws 从 iostream 中删除前导空白,不论是否启用了跳过功能。以下示例说明了如何从 iostream istr 中删除前导空白:
istr >> ws; |
按照约定,第一个参数为非零错误状态的提取器不能从输入流提取任何数据,且不能清除任何错误位。失败的提取器至少应该设置一个错误位。
对于输出错误,您应该定期检查错误状态,并在发现非零状态时采取某些操作(诸如终止)。! 运算符测试 iostream 的错误状态。例如,如果输入字母字符用于输入,以下代码就会产生输入错误:
#include <stdlib.h> #include <iostream.h> void error (const char* message) { cerr << message << "\n"; exit(1); } int main() { cout << "Enter some characters: "; int bad; cin >> bad; if (!cin) error("aborted due to input error"); cout << "If you see this, not an error." << "\n"; return 0; } |
类 ios 具有可用于错误处理的成员函数。详细信息请参见手册页。
可以将 stdio 用于 C++ 程序,但在程序内的同一标准流中混合使用 iostream 与 stdio 时,可能会发生问题。例如,如果同时向 stdout 和 cout 写入,会发生独立缓冲,并产生不可预料的结果。如果从 stdin 与 cin 两者进行输入,问题会更加严重,因为独立缓冲可能会破坏输入。
要消除标准输入、标准输出和标准错误中的这种问题,就请在执行输入或输出前使用以下指令:它将所有预定义的 iostream 与相应的预定义 stdio 文件连接起来。
ios::sync_with_stdio(); |
因为在预定义流作为连接的一部分成为无缓冲流时,性能会显著下降,所以该连接不是缺省连接。可以在应用于不同文件的同一程序中同时使用 stdio 和 iostream。也就是说,可以使用 stdio 例程向 stdout 写入,并向连接到 iostream 的其他文件写入。可以打开 stdio 文件进行输入,也可以从 cin 读取,只要不同时尝试从 stdin 读取即可。
要读取或写入不是预定义的 iostream 的流,需要创建自己的 iostream。通常,这意味着创建 iostream 库中所定义类型的对象。本节讨论了可用的各种类型:
处理文件类似于处理标准输入和标准输出;类 ifstream、ofstream 和 fstream 分别从类 istream、ostream 和 iostream 派生而来。作为派生的类,它们继承了插入和提取运算符(以及其他成员函数),还有与文件一起使用的成员和构造函数。
可将文件 fstream.h 包括进来以使用任何 fstream。如果只要执行输入,请使用 ifstream;如果只要执行输出,请使用 ofstream;如果要对流执行输入和输出,请使用 fstream。将文件名称用作构造函数参数。
例如,将文件 thisFile 复制到文件 thatFile,如以下示例所示:
ifstream fromFile("thisFile"); if (!fromFile) error("unable to open ’thisFile’ for input"); ofstream toFile ("thatFile"); if (!toFile) error("unable to open ’thatFile’ for output"); char c; while (toFile && fromFile.get(c)) toFile.put(c); |
该代码:
使用 ios::in 的缺省模式创建名为 fromFile 的 ifstream 对象,并将其连接到 thisFile。然后打开 thisFile。
检查新的 ifstream 对象的错误状态,如果它处于失败状态,则调用 error 函数(必须在程序中其他地方定义)。
使用 ios::out 的缺省模式创建名为 toFile 的 ofstream 对象,并将其连接到 thatFile。
按上文所述检查 toFile 的错误状态。
创建 char 变量用于存放传递的数据。
将 fromFile 的内容复制到 toFile,每次一个字符。
当然这种方法(每次复制一个字符)不适用于复制文件。该代码只是 fstream 使用示例。应该将与输入流关联的 streambuf 插入输出流中。请参见13.10 streambuf 和手册页 sbufpub(3CC4)。
该模式由枚举类型 open_mode 中的 or-ing 位构造,它是类 ios 的公有类型,其定义如下:
enum open_mode {binary=0, in=1, out=2, ate=4, app=8, trunc=0x10, nocreate=0x20, noreplace=0x40}; |
UNIX 中不需要 binary 标志,提供该标志是为了与需要它的系统兼容。可移植代码在打开二进制文件时要使用 binary 标志。
您可以打开文件同时用于输入和输出。例如,以下代码打开了文件 someName 用于输入和输出,同时将其连接到 fstream 变量 inoutFile。
fstream inoutFile("someName", ios::in|ios::out); |
可以在未指定文件的情况下声明 fstream,并在以后打开该文件。例如,以下代码创建了 ofstream toFile,以便进行写入。
ofstream toFile; toFile.open(argv[1], ios::out); |
可以关闭 fstream,然后使用另一文件打开它。例如,要在命令行上处理提供的文件列表:
ifstream infile; for (char** f = &argv[1]; *f; ++f) { infile.open(*f, ios::in); ...; infile.close(); } |
如果了解文件描述符(如整数 1 表示标准输出),可以使用如下代码打开它:
ofstream outfile; outfile.attach(1); |
如果通过向 fstream 构造函数之一提供文件名或使用 open 函数来打开文件,则在销毁 fstream(通过 delete 销毁或其超出作用域)时,会自动关闭该文件。将文件 attach 到 fstream 时,不会自动关闭该文件。
您可以在文件中改变读取和写入的位置。有多个工具可以达到这个目的。
tellg (tellp) 是报告文件位置的 istream (ostream) 成员函数。因为 istream 和 ostream 是 fstream 的父类,所以 tellg 和 tellp 还可以作为 fstream 类的成员函数调用。
seekg (seekp) 是查找给定位置的 istream (ostream) 成员函数。
seek_dir enum 指定相对位置以用于 seek。
enum seek_dir {beg=0, cur=1, end=2}; |
例如,给定 fstream aFile:
streampos original = aFile.tellp(); //save current position aFile.seekp(0, ios::end); //reposition to end of file aFile << x; //write a value to file aFile.seekp(original); //return to original position |
seekg (seekp) 可以采用一个或两个参数。如果有两个参数,第一个参数是相对于 seek_dir 值(也就是第二个参数)指示的位置的位置。例如:
aFile.seekp(-10, ios::end); |
从终点移动 10 个字节
aFile.seekp(10, ios::cur); |
从当前位置向前移 10 个字节。
并不能方便地在文本流中进行任意查找,但总是可以返回到以前保存的 streampos 值。
iostream 不允许将一个流赋值给另一个流。
复制流对象的问题是有两个可以独立更改的状态信息版本,例如输出文件中指向当前写入位置的指针。因此,某些问题会发生。
操纵符是可以在 iostream 中插入或提取以起到特殊作用的值。
参数化操纵符是具有一个或多个参数的操纵符。
因为操纵符是普通的标识符,因此会用完可能的名称,而 iostream 不会为每个可能的函数定义操纵符。本章的其他部分讨论了各种操纵符和成员函数。
有 13 个预定义的操纵符,如表 13–2 中所述。使用该表时,要进行以下假设:
i 的类型为 long。
n 的类型为 int。
c 的类型为 char。
istr 是输入流。
ostr 是输出流。
|
预定义的操纵符 |
说明 |
---|---|---|
1 |
ostr << dec、istr >> dec | |
2 |
ostr << endl | |
3 |
ostr << ends | |
4 |
ostr << flush | |
5 |
ostr << hex、istr >> hex | |
6 |
ostr << oct、istr >> oct | |
7 |
istr >> ws | |
8 |
ostr << setbase(n)、istr >> setbase(n) | |
9 |
ostr << setw(n)、istr >> setw(n) | |
10 |
ostr << resetiosflags(i)、istr >> resetiosflags(i) | |
11 |
ostr << setiosflags(i)、istr >> setiosflags(i) | |
12 |
ostr << setfill(c)、istr >> setfill(c) | |
13 |
ostr << setprecision(n)、istr >> setprecision(n) |
要使用预定义的操纵符,必须在程序中包含文件 iomanip.h。
您可以定义自己的操纵符。操纵符共有两个基本类型:
无格式操纵符-采用 istream&、ostream& 或 ios& 参数,对流进行操作,然后返回其参数。
参数化操纵符-采用 istream&、ostream& 或 ios& 参数以及一个附加参数,对流进行操作,然后返回其流参数。
无格式操纵符是具有如下功能的函数:
执行到流的引用
以某种方式操作流
返回操纵符的参数
由于为 iostream 预定义了采用(指向)此类函数(的指针)的移位运算符,因此可以在输入或输出运算符序列中放入函数。移位运算符会调用函数而不是尝试读取或写入值。例如,下面是将 tab 插入 ostream 的 tab 操纵符:
ostream& tab(ostream& os) { return os <<’\t’; } ... cout << x << tab << y; |
详细描述实现以下操作的方法:
const char tab = ’\t’; ... cout << x << tab << y; |
下面示例显示了无法用简单常量来实现的代码。假设要对输入流打开或关闭空白跳过功能。可以分别调用 ios::setf 和 ios::unsetf 来打开和关闭 skipws 标志,也可以定义两个操纵符。
#include <iostream.h> #include <iomanip.h> istream& skipon(istream &is) { is.setf(ios::skipws, ios::skipws); return is; } istream& skipoff(istream& is) { is.unsetf(ios::skipws); return is; } ... int main () { int x,y; cin >> skipon >> x >> skipoff >> y; return 1; } |
iomanip.h 中包含的其中一个参数化操纵符是 setfill。setfill 设置用于填写字段宽度的字符。该操作按照下例所示实现:
//file setfill.cc #include<iostream.h> #include<iomanip.h> //the private manipulator static ios& sfill(ios& i, int f) { i.fill(f); return i; } //the public applicator smanip_int setfill(int f) { return smanip_int(sfill, f); } |
参数化操纵符的实现分为两部分:
操纵符。它使用一个额外的参数。在上面的代码示例中,采用了额外的 int 参数。由于未给这个操纵符函数定义移位运算符,所以您无法将它放至输入或输出操作序列中。相反,您必须使用辅助函数 applicator。
applicator。它调用该操纵符。applicator 是全局函数,您会为它生成在头文件中可用的原型。通常操纵符是文件中的静态函数,该文件包含了 applicator 的源代码。只有 applicator 可以调用该操纵符,如果您将操纵符设置为静态,就要使操纵符名称始终位于全局地址空间之外。
头文件 iomanip.h 中定义了多个类。每个类都保存一个操纵符函数的地址和一个参数的值。manip(3CC4) 手册页中介绍了 iomanip 类。上面的示例使用了 smanip_int 类,它是与 ios 一起使用。因为该类与 ios 一起使用,所以也可以与 istream 和 ostream 一起使用。上面的示例还使用了另一个类型为 int 的参数。
applicator 创建并返回类对象。在上面的代码示例中,类对象是 smanip_int,其中包含了操纵符和 applicator 的 int 参数。iomanip.h 头文件定义了用于该类的移位运算符。如果 applicator 函数 setfill 在输入或输出操作序列中,会调用该 applicator 函数,且其返回一个类。移位运算符作用于该类,以使用其参数值(存储在类中)调用操纵符函数。
在以下示例中,操纵符 print_hex:
使用类 omanip_long 的原因是该代码示例仅用于输出,而且操作对象是 long 而不是 int:
#include <iostream.h> #include <iomanip.h> static ostream& xfield(ostream& os, long v) { long save = os.setf(ios::hex, ios::basefield); os << v; os.setf(save, ios::basefield); return os; } omanip_long print_hex(long v) { return omanip_long(xfield, v); } |
请参见 strstream(3CC4) 手册页。
请参见 stdiobuf(3CC4) 手册页。
iostream 是由两部分(输入或输出)构成的系统的格式化部分。系统的其他部分由处理无格式字符流的输入或输出的 streambuf 组成。
通常,可以通过 iostream 使用 streambuf,因此,不必担心 streambuf 的细节。您可以选择直接使用 streambuf,例如,如果需要提高效率或解决 iostream 内置的处理或格式化错误。
streambuf 由字符流或字符序列和一个或两个指向相应序列的指针组成。每个指针都指向两个字符间。(实际上,指针无法指向字符间,但可以按这种方式考虑指针。)有两种 streambuf 指针:
streambuf 可以有其中一个指针,也可以两个全有。
可以使用多种方法来操作指针的位置和序列的内容。操作两个指针时它们是否都会移动取决于使用 streambuf 种类。通常,如果使用队列式 streambuf,get 和 put 指针独立移动;如果使用文件式 streambuf,get 和 put 指针总是一起移动。例如,strstream 是队列式流,fstream 是文件式流。
从来不创建实际的 streambuf 对象,而是只创建从 streambuf 类派生的类的对象。例如 filebuf 和 strstreambuf,filebuf(3CC4) 手册页和 ssbuf(3) 手册页中分别对它们进行了介绍。高级用户可能想从 streambuf 派生自己的类,以便提供特定设备的接口或提供基本缓冲以外的功能。sbufpub(3CC4) 和 sbufprot(3CC4) 手册页中讨论了如何执行此操作。
除了创建自己的特殊种类的 streambuf 外,您可能还想通过访问与 iostream 关联的 streambuf 来访问公用成员函数(如上面引用的手册页中所述)。此外,每个 iostream 都有采用 streambuf 指针的已定义插入器和提取器。插入或提取 streambuf 时,会复制整个流。
下面是另一种文件复制(前面讨论过)方法,这里为了清晰起见省略了错误检查:
ifstream fromFile("thisFile"); ofstream toFile ("thatFile"); toFile << fromFile.rdbuf(); |
按照前面所述的方式打开输入和输出文件。每个 iostream 类都有成员函数 rdbuf,它返回指向与其关联的 streambuf 对象的指针。如果是 fstream,则 streambuf 对象是类型 filebuf。与 fromFile 关联的整个文件都复制到(插入)与 toFile 关联的文件。最后一行也可以改写为:
fromFile >> toFile.rdbuf(); |
然后源文件被提取到目标中。两种方法是完全等同的。
许多 C++ 手册页都介绍了 iostream 库的详细信息。下表概述了每个手册页中的内容。
要访问传统 iostream 库手册页,请键入:
example% man -s 3CC4 name |
iostream 库说明中常常使用一些与一般编程中的术语类似的术语,但有特殊含义。下表定义了讨论 iostream 库时使用的这些术语。
表 13–4 iostream 术语
iostream 术语 |
定义 |
---|---|
该词有两个含义,一个特定于 iostream 软件包,另一个较常适用于输入和输出。 与 iostream 库特定相关时,缓冲区是由类 streambuf 定义的类型的对象。 通常,缓冲区是一个内存块,用于将字符高效传输到输出的输入。对于已缓冲的 I/O,缓冲区已满或被强制刷新之前,字符的实际传输会延迟。 无缓冲的缓冲区是指在其中没有上文定义的通用意义的缓冲区的 streambuf。本章避免使用缓冲区一词来指 streambuf。但是,手册页和其他 C++ 文档使用缓冲区一词来表示 streambuf。 |
|
从 iostream 获取输入的过程。 |
|
专用于文件的输入或输出流。特指以 courier 字体输出时从类 iostream 派生的类。 |
|
将输出发送到 iostream 中的过程。 |
|
通常为输入或输出流。 |
|
iostream 库 |
通过 include 文件 iostream.h、fstream.h、strstream.h、iomanip.h 和 stdiostream.h 实现的库。因为 iostream 是面向对象的库,所以应扩展该库。因此,可以对 iostream 库执行的某些操作并未实现。 |
通常是指 iostream、fstream、strstream 或用户定义的流。 |
|
包含字符序列的缓冲区,其中字符具有 put 或 get 指针(或兼有)。以 courier 字体输出时,它表示特定类。否则,通常是指 streambuf 类或从 streambuf 派生的类的对象。任何流对象都包含从 streambuf 派生的类型的对象或指向对象的指针。 |
|
专用于字符数组的 iostream。它是指以 courier 字体输出时的特定类。 |
3.2 + 4i 1 + 3i 1 + 2.3i
在特例情况下,如 0 + 3i 是纯虚数,通常写为 3i;5 + 0i 是纯实数,通常写为 5。可以使用 complex 数据类型来表示复数。
复数运算库 (libcomplex) 仅可用于兼容模式 ( -compat[ =4])。在标准模式(缺省模式)下,C++ 标准库 libCstd 附带具有类似功能的复数类。
运算符
数学库函数(为内建数字类型定义)
扩展(用于允许复数输入和输出的 iostream)
错误处理机制
复数也可表示为绝对值(或幅度)和参数(或角度)。该库提供了在实部虚部(笛卡尔)表示形式和幅度角度(极)表示形式之间进行转换的函数。
数字复共轭的虚部中有相反符号。
要使用复数库,应在程序中包含头文件 complex.h,并使用 -library=complex 选项进行编译和链接。
复数运算库定义了一个类:complex 类。complex 类的对象可以存放一个复数。复数由两部分构成:
实部
虚部
class complex { double re, im; }; |
complex 类的对象值是一对 double 值。第一个值表示实部,第二个值表示虚部。
有两个用于 complex 的构造函数。它们的定义是:
complex::complex() {re=0.0; im=0.0;} complex::complex(double r, double i = 0.0) {re=r; im=i;} |
如果声明复数变量时没有指定参数,则会使用第一个构造函数并初始化变量,因此两个部分都为 0。以下示例创建了一个其实部和虚部均为 0 的复数变量。
complex aComp; |
您可以给定一个或两个参数。无论是以上哪种情况,都将使用第二个构造函数。如果只给定一个参数,该参数将作为实部的值,而虚部的值设置为 0。例如:
complex aComp(4.533); |
用下列值创建一个复数变量:
4.533 + 0i |
如果给定了两个值,第一个值被视为实部的值,而第二个值被视为虚部的值。例如:
complex aComp(8.999, 2.333); |
用下列值创建一个复数变量:
8.999 + 2.333i |
也可以使用复数运算库中提供的 polar 函数(请参见14.3 数学函数)创建复数。polar 函数根据给定的极坐标幅度和角度创建复数值。
没有用于 complex 类型的析构函数。
复数运算库定义了所有基本的算术运算符。具体来说,以下运算符按一般方法和普通的优先级工作:
+ - / * =
减法运算符 (-) 具有其通常的二元和一元含义。
此外,您可以按通常的方法使用以下运算符:
加法赋值运算符 (+=)
减法赋值运算符 (-=)
乘法赋值运算符 (*=)
除法赋值运算符 (/=)
但是,若将以上四个运算符用于表达式,则不产生任何值。例如,下列表达式无法进行运算:
complex a, b; ... if ((a+=2)==0) {...}; // illegal b = a *= b; // illegal |
另外还可以使用等号 (==) 和不等号 (!=),它们具有常规含义。
将运算表达式中的实数和复数混合时,C++ 使用复数运算符函数并将实数转换为复数。
复数运算库提供了许多数学函数。一些是专用于复数的,而其余的则是标准 C 数学库中函数的复数版本。
全部这些函数为每个可能的参数产生结果。如果函数无法生成具有数学意义的结果,它就调用 complex_error 并返回适用的某值。具体来说,这些函数会尽量避免实际的溢出,而是调用 complex_error 并显示消息。下表描述了复数运算库函数的提示。
sqrt 和 atan2 函数的实现遵循 C99 csqrt 附录 G 规范。
复数运算库函数 |
说明 |
---|---|
double abs(const complex) |
返回复数的幅度。 |
double arg(const complex) |
返回复数的角度。 |
complex conj(const complex) |
返回其参数的复共轭。 |
double imag(const complex&) |
返回复数的虚部。 |
double norm(const complex) |
返回其参数幅度的平方。比 abs 快,但较易产生溢出。用于比较幅度。 |
complex polar(double mag, double ang=0.0) |
执行一对表示复数幅度和角度的极坐标,并返回对应的复数。 |
double real(const complex&) |
返回复数的实部。 |
表 14–2 复数数学函数和三角函数
说明 |
|
---|---|
complex acos(const complex) |
返回余弦为其参数的角度。 |
complex asin(const complex) |
返回正弦为其参数的角度。 |
complex atan(const complex) |
返回正切为其参数的角度。 |
complex cos(const complex) |
返回其参数的余切。 |
complex cosh(const complex) |
返回其参数的双曲余弦。 |
complex exp(const complex) |
计算 e**x,其中 e 为自然对数的基数,x 是为 exp 提供的参数。 |
complex log(const complex) |
返回其参数的自然对数。 |
complex log10(const complex) |
返回其参数的常用对数。 |
complex pow(double b, const complex exp) complex pow(const complex b, int exp) complex pow(const complex b, double exp) complex pow(const complex b, const complex exp) |
使用两个参数:pow(b, exp ).它计算出 b 的 exp 次幂。 |
complex sin(const complex) |
返回其参数的正弦。 |
complex sinh(const complex) |
返回其参数的双曲正弦。 |
complex sqrt(const complex) |
返回其参数的平方根。 |
complex tan(const complex) |
返回其参数的正切。 |
complex tanh(const complex) |
返回其参数的双曲正切。 |
extern int errno; class c_exception {...}; int complex_error(c_exception&); |
外部变量 errno 是来自 C 库的全局错误状态。errno 可以为标准头文件 errno.h 中所列值(请参见 perror(3) 手册页)。没有任何函数会将 errno 设置为零,但有许多函数会将它设置为其他值。
要分辨特定运算是否失败:
在运算前将 errno 设置为零。
测试运算。
函数 complex_error 采用对类型 c_exception 的引用并由下列复数运算库函数调用:
exp
log
log10
sinh
cosh
缺省版本的 complex_error 返回零。这个零值的返回意味着发生了缺省的错误处理。可以提供自己的替换函数 complex_error,以执行其他错误处理。cplxerr(3CC4) 手册页中介绍了错误处理。
cplxtrig(3CC4) 和 cplxexp(3CC4) 手册页中介绍了缺省的错误处理,下表中也进行了简要介绍。
复数运算库函数 |
缺省错误处理汇总 |
---|---|
exp | |
log、log10 | |
sinh、cosh |
如果参数的虚部产生溢出,则返回一个零复数。如果实部产生溢出,则返回一个极大的复数。无论是以上哪种情况,都将 errno 设置为 ERANGE。 |
复数运算库提供了用于复数的缺省提取器和插入器,如以下示例所示:
ostream& operator<<(ostream&, const complex&); //inserter istream& operator>>(istream&, complex&); //extractor |
有关提取器和插入器的基本信息,请参见13.2 iostream 交互的基本结构和13.3.1 使用 iostream 进行输出。
对于输入,复数提取器 >> 从输入流中提取一对数(用圆括号括住,并由逗号分隔开),并将其读入复数对象。第一个值被视为实部的值,而第二个值被视为虚部的值。例如,给定声明和输入语句:
complex x; cin >> x; |
以及输入 (3.45, 5),则 x 值等于 3.45 + 5.0i。对插入器来讲反向为真。如果给定 complex x(3.45, 5),cout<<x 将输出 (3.45, 5)。
输入通常由括号中的一对数值(由逗号分隔)组成,也可选择空格。如果您提供一个单一数值(具有或不具有括号和空格),那么提取器会将数值的虚部设置为零。不要将符号 i 包括在输入文本中。
插入器会插入括号中实部和虚部的值(由逗号分隔)。它不包括符号 i。这两个值都视为 double。
类型 complex 专门用于处理混合模式表达式中的内置运算类型。运算类型会缺省转换为 complex 类型,而且算术运算符和大多数数学函数都有 complex 版本。例如:
int i, j; double x, y; complex a, b; a = sin((b+i)/y) + x/j; |
表达式 b+i 是混合模式。整数 i 通过构造函数 complex::complex(double,double=0) 转换为类型 complex(整型数先是转换为类型 double)。所得结果除以 double 类型的 y,因此 y 也转换为 complex 且使用了复数除法运算。这样,得到的商是 complex 类型,因此调用复数正弦例程,从而生成另一个 complex 结果等。
但是,并非所有的数学运算符和转换都是暗示的(即使定义)。例如从数学角度,复数未较好排序,只能比较等式。
complex a, b; a == b; // OK a != b; // OK a < b; // error: operator < cannot be applied to type complex a >= b; // error: operator >= cannot be applied to type complex |
同样,由于未明确定义概念,因此不会自动将类型 complex 转换为其他类型。您可以指定是否需要实部、虚部或幅度。
complex a; double f(double); f(abs(a)); // OK f(a); // error: no match for f(complex) |
设计 complex 类主要为了提高效率。
最简单的函数声明为 inline,以消除函数调用开销。
在函数不同时就会提供函数的多个开销版本。例如,pow 函数有多个版本,分别取类型为 double 和 int 以及 complex 的指数,而前者的运算简单得多。
在包含 complex.h 时,会自动包含标准 C 数学库头文件 math.h。然后 C++ 开销规则就会产生类似于下面的表达式效率评估:
double x; complex x = sqrt(x); |
在此示例中,调用了标准数学函数 sqrt(double),且结果转换为 complex 类型,而不是先转换为 complex 类型再调用 sqrt(complex)。该结果转向重载决策规则的外部,正好是您所希望的结果。
复数运算库的剩余文档由下表所列的手册页组成:
表 14–3 有关 complex 类型的手册页
手册页 |
概述 |
---|---|
cplx.intro(3CC4) |
对复数运算库的一般性介绍 |
cartpol(3CC4) |
笛卡尔函数和极函数 |
cplxerr(3CC4) |
错误处理函数 |
cplxexp(3CC4) |
指数、对数和平方根函数 |
cplxops(3CC4) |
算术运算符函数 |
cplxtrig(3CC4) |
三角函数 |
本章解释了如何生成您自己的库。
库具有两点好处。首先,它们提供了在多个应用程序间共享代码的方法。如果您有要共享的代码,则可以创建一个具有该代码的库,并将该库链接到需要这些代码的应用程序。其次,库提供了降低大型应用程序复杂性的方法。这类应用程序可以将相对独立的部分生成为库并进行维护,因此减轻程序员在其他部分工作的负担。
生成库只不过是创建 .o 文件(使用 -c 选项编译代码)并使用 CC 命令将 .o 文件并入库中。可以生成两种库:静态(归档)库和动态(共享)库。
对于静态(归档)库,库中的对象在链接时链接到程序的可执行文件中。只有库中属于应用程序所需的那些 .o 文件链接到可执行文件。静态(归档)库名称通常以 .a 后缀结尾。
对于动态(共享)库,库中的对象并不链接到程序的可执行文件,而是链接程序在可执行文件中注明程序依赖于库。执行该程序时,系统会装入程序所需的动态库。如果使用同一动态库的两个程序同时执行,那么操作系统在程序间共享这个动态库。动态(共享)库名称以 .so 后缀结尾。
动态链接共享库较静态链接归档库有多个优势:
可执行文件较小。
在运行时,代码的有效部分可在程序间共享,这样就可以降低内存使用量。
库可以在运行时替换,无需重新链接应用程序。(动态链接共享库的主要机制是使程序能够利用 Solaris 操作系统的多项改进的功能,而无需重新链接和分发程序。)
但动态库也具有一些缺点:
运行时链接有执行时间成本。
使用动态库进行程序的分发可能会要求同时分发该程序所使用的库。
将共享库移动到一个不同的位置就可以阻止系统查找该库并执行程序。(环境变量 LD_LIBRARY_PATH 可以帮助克服此问题。)
生成静态(归档)库的机制与生成可执行文件相似。可以使用 CC 的 – xar 选项将目标 (.o) 文件集合并入单个库中。
可以使用 CC -xar 而非直接使用 ar 命令来生成静态(归档)库。C++ 语言通常要求编译器维护的信息比传统 .o 文件提供的信息多,尤其是模板实例。– xar 选项可确保所有必要信息(包括模板实例)都包括在库中。在通常的编程环境下,可能无法完成该操作,因为 make 无法确定实际创建和引用了哪些模板文件。如果没有 CC -xar,引用的模板实例可能没有按照需要包括在库中。例如:
% CC -c foo.cc # Compile main file, templates objects are created. % CC -xar -o foo.a foo.o # Gather all objects into a library. |
–xar 标志会使 CC 创建静态(归档)库。要为新建的库命名,需要使用 – o 指令。编译器检查命令行上的目标文件,交叉引用这些目标文件与模板系统信息库中的目标文件,并将用户的目标文件所需的模板(以及主目标文件本身)添加到归档文件中。
仅可将 -xar 标志用于创建或更新现有归档文件。不要用它来维护归档。-xar 选项与 ar -cr 等效。
最好每个 .o 文件中只有一个函数。如果要链接归档文件,则在需要该归档文件中的特定 .o 文件中的符号时,整个 .o 文件都链接到应用程序中。每个 .o 文件中有一个函数可以确保将只从归档文件链接应用程序所需的那些符号。
动态(共享)库的生成方式与静态(归档)库的生成方式基本相同,除了在命令行上使用 –G 而不是 –xar。
不应直接使用 ld。与静态库一样,CC 命令可以确保使用模板时,模板系统信息库中所有必要的模板实例都包括在库中。在执行 main() 之前会调用与应用程序链接的动态库中所有静态构造函数,在 main() 退出之后会调用所有静态析构函数。如果使用 dlopen() 打开共享库,所有静态构造函数都在执行 dlopen() 时执行,所有静态析构函数都在执行 dlclose() 时执行。
应该使用 CC -G 来生成动态库。使用 ld(链接编辑器)或 cc(C 编译器)生成动态库时,异常可能无法生效,且库中定义的全局变量未初始化。
要生成动态(共享)库,必须使用 CC 的 –Kpic 或 –KPIC 选项编译每个对象来创建可重定位的目标文件。然后您就可以生成一个具有这些可重定位目标文件的动态库。如果遇到异常的链接失败,可能是忘记了使用 –Kpic 或 –KPIC 编译某些对象。
要生成名为 libfoo.so 的 C++ 动态库(该库包含源文件 lsrc1.cc 和 lsrc2.cc 中的对象),请键入:
% CC -G -o libfoo.so -h libfoo.so -Kpic lsrc1.cc lsrc2.cc |
-G 选项指定动态库的构造。-o 选项指定库的文件名。-h 选项指定共享库的内部名称。-Kpic 选项指定目标文件与位置无关。
CC -G 命令不会将任何 -l 选项传递给链接程序 ld。为了确保正确的初始化顺序,共享库对其所需的每个其他共享库必须具有显式的依赖性。要创建依赖性,请对每个此类库使用 -l 选项。典型的 C++ 共享库将使用以下几组选项之一:
-lCstd -lCrun -lc -library=stlport4 -lCrun -lc |
为了确保列出了需要的所有依赖性,请使用 -zdefs 选项生成库。对于缺少的每个符号定义,链接程序都会发出错误消息。要提供缺少的定义,请针对这些库添加 -l 选项。
要确定是否包含了不需要的依赖性,请使用以下命令
ldd -u -r mylib.so ldd -U -r mylib.so |
然后可以重新生成没有不需要的依赖性的 mylib.so。
对于包含 C++ 代码的程序,切勿使用 -Bsymbolic,而应使用链接程序映射文件。如果使用 -Bsymbolic,不同模块中的引用会绑定到应是一个全局对象内容的不同副本。
异常机制依赖对地址的比较。如果您具有某项内容的两个副本,它们的地址就不等同且异常机制可能失败,这是由于异常机制依赖对假设为唯一地址内容的比较。
在组织生成一个仅供内部使用的库时,可以使用不建议在一般情况下使用的选项来生成这个库。具体来说,库不需要符合系统的应用程序二进制接口 (application binary interface, ABI)。例如,可以使用 -fast 选项编译库,以提高其在某已知体系结构上的性能。同样,可以使用 -xregs=float 选项编译库以提高性能。
在组织生成一个供其他公司使用的库时,库的管理、平台的一般性以及其他问题就变得尤为重要。一个用于检验库是否为公用的简单测试就是询问应用程序程序员是否可以轻松地重新编译该库。生成公用库时应该符合系统的应用程序二进制接口 (application binary interface, ABI)。通常,这意味着应该避免任何特定于处理器的选项。(例如,不使用 –fast 或 –xtarget。)
SPARC ABI 为应用程序保留了一些专用寄存器。对于 V7 和 V8,这些寄存器是 %g2、%g3 和 %g4。对于 V9,这些寄存器是 %g2 和 %g3。由于多数编译用于应用程序,所以在缺省情况下,为了提高程序的性能,C++ 编译器将这些寄存器作为临时寄存器使用。但是,对公用库中寄存器的使用通常不兼容于 SPARC ABI。生成公用库时,请使用 -xregs=no%appl 选项编译所有对象,以确保不会使用应用程序寄存器。
如果要生成以 C++ 编写但可用于 C 程序的库,必须创建 C API(application programming interface,应用程序编程接口)。为此,应先使所有导出的函数为 extern "C"。注意,只有在全局函数中才能够完成该操作,在成员函数中不行。
如果 C 接口库需要 C++ 运行时支持,且要使用 cc 进行链接,则在使用 C 接口库时,还必须用 libC(兼容模式)或 libCrun(标准模式)链接应用程序。(如果 C 接口库不需要 C++ 运行时支持,就不必用 libC 或 libCrun 进行链接。)归档库与共享库的链接步骤是不同的。
提供归档的 C 接口库时,必须提供如何使用该库的说明。
如果 C 接口库是在标准模式(缺省模式)下使用 CC 生成的,那么在使用该 C 接口库时,将 -lCrun 添加到 cc 命令行。
如果 C 接口库是在兼容模式 (-compat) 下使用 CC 生成的,那么在使用该 C 接口库时,将 -lC 添加到 cc 命令行。
提供共享的 C 接口库时,必须在生成库时创建对 libC 或 libCrun 的依赖性。如果共享库具有正确的依赖性,就不必在使用该库时将 -lC 或 -lCrun 添加到命令行。
如果要在兼容模式 (-compat) 下生成 C 接口库,应在生成库时将 -lC 添加到 CC 命令行。
如果要在标准模式(缺省模式)下生成 C 接口库,应在生成库时将 -lCrun 添加到 CC 命令行。
如果要删除对 C++ 运行时库的任何依赖性 ,应该在库源文件中强制应用下列代码规则:
不要使用任何形式的 new 或 delete,除非提供了自己的相应版本。
不要使用异常。
不要使用运行时类型信息 (RTTI)。
如果要使用 dlopen() 从 C 程序打开 C++ 共享库,应确保共享库依赖于适当的 C++ 运行时(对于 -compat=4,为 libC.so.5;对于 -compat=5,为 libCrun.so.1)。
为此,应在生成共享库时,将 -lC(对于 -compat=4)或 lCrun(对于 -compat=5)添加到命令行。例如:
example% CC -G -compat=4... -lC example% CC -G -compat=5... -lCrun |
如果共享库使用了异常且不具有对 C++ 运行库的依赖性,则 C 程序可能会出现无规律的行为。