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

第 3 部分 库

第 11 章 使用库

库提供了在多个应用程序间共享代码的方法,也提供了减小超大型应用程序复杂度的方法。C++ 编译器使您可以访问各种库。本章说明了如何使用这些库。

11.1 C 库

Oracle Solaris 操作系统附带了安装在 /usr/lib 中的几个库。这些库大多有 C 接口。缺省情况下,其中的 libclibm 库通过 CC 驱动程序进行链接。如果使用 –mt 选项,则链接 libthread 库。如果要链接其他系统库,请在链接时使用适当的 –l 选项。例如,要链接 libdemangle 库,请在链接时在 CC 命令行上传递 –ldemangle


example% CC text.c -ldemangle

C++ 编译器具有自己的运行时支持库。所有 C++ 应用程序都由 CC 驱动程序链接到这些库。C++ 编译器还具有其他一些有用的库,如下节所述。

11.2 随 C++ 编译器提供的库

C++ 编译器附带了一些库。其中一些只能在兼容模式 (-compat=4) 下使用,有些只能在标准模式 (–compat=5) 下使用,有些可以在这两种模式下使用。libgclibdemangle 库都有 C 接口,可以在任何模式下链接到应用程序。

下表列出了随 C++ 编译器提供的库,以及可以使用这些库的模式。

表 11–1 C++ 编译器附带的库

库 

说明 

可用模式 

libstlport

标准库的 STLport 实现。 

–compat=5

libstlport_dbg

调试模式的 STLport 库 

–compat=5

libCrun

C++ 运行时 

–compat=5

libCstd

C++ 标准库 

–compat=5

libiostream

传统 iostream

–compat=5

libC

C++ 运行时,传统 iostream

–compat=4

libcsunimath

支持 -xia 选项

–compat=5

libcomplex

复数库 

–compat=4

librwtool

Tools.h++ 7

–compat=4– compat=5

librwtool_dbg

支持调试的 Tools.h++ 7

–compat=4、–compat=5

libgc

垃圾收集 

C 接口 

libdemangle

还原 

C 接口 


注 –

请勿重新定义或修改用于 STLport、Rogue Wave 或 Solaris Studio C++ 库的任何配置宏。库是按照适用于 C++ 编译器的方式进行配置和生成的。libCstd 和 Tool.h++ 配置为可互操作,因此,修改配置宏会导致程序不能编译、不能链接或不能正常运行。


11.2.1 C++ 库描述

以下是这些库中每个库的简单描述。

11.2.2 访问 C++ 库的手册页

与本节所述库关联的手册页位于第 1、3、3C++ 和 3cc4 节中。

要访问 C++ 库的手册页,请输入:


example% man library-name

要访问 C++ 库版本 4.2 的手册页,请输入:


example% man -s 3CC4 library-name

11.2.3 缺省 C++ 库

缺省情况下,CC 驱动程序会链接其中一些 C++ 库,而其他库需要显式链接。在标准模式下,CC 驱动程序缺省链接下列库:

-lCstd -lCrun -lm -lc

在兼容模式 (-compat) 下,缺省链接下列库:

-lC -lm -lc

有关更多信息,请参见A.2.49 -library=l[ ,l...]

11.3 相关的库选项

CC 驱动程序提供了一些选项来帮助用户使用库。


example% CC test.cc -library=rwtools7,iostream

example% CC test.cc -library=gc -staticlib=gc

example% CC test.cc -compat=4 -staticlib=libC

example% CC test.cc -library=no%Crun,no%Cstd

缺省情况下,CC 根据命令行选项链接不同的系统库集合。如果指定 -xnolib(或 -nolibCC 仅链接在命令行上使用 -l 选项显式指定的那些库。(如果使用 -xnolib-nolib,会忽略 -library 选项(如果有)。)

使用 –R 选项可以在可执行文件中生成动态库搜索路径。执行期间,运行时链接程序使用这些路径搜索应用程序所需的共享库。缺省情况下,CC 驱动程序将 – R<install_directory>/lib 传递给 ld(如果编译器安装在标准位置中)。可以使用 -norunpath 禁止在可执行文件中生成共享库的缺省路径。

缺省情况下,链接程序会搜索 /lib/usr/lib。请勿在 -L 选项中指定这些目录或任何编译器安装目录。

对于针对部署生成的程序,应该使用 -norunpath-R 选项进行生成,这样可避免在编译器目录中查找库。(请参见11.6 使用共享库)。

11.4 使用类库

通常,使用类库分两个步骤:

  1. 在源码中包括适当的头文件。

  2. 将程序与目标库链接。

11.4.1 iostream

C++ 编译器提供两种 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++ 迁移指南》。

11.4.2 complex

标准库提供了模板化的 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

11.4.3 链接 C++ 库

下表列出了链接 C++ 库的编译器选项。有关更多信息,请参见A.2.49 -library=l[ ,l...]

表 11–2 链接 C++ 库的编译器选项

库 

编译模式 

选项  

传统 iostream

–compat=4

–compat=5

不需要 

-library=iostream

complex

–compat=4

-compat=5

-library=complex

不需要 

Tools.h++ 版本 7

–compat=4

–compat=5

-library=rwtools7

-library=rwtools7,iostream

-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

11.5 静态链接标准库

CC 驱动程序在缺省情况下链接几个库的共享版本(包括 libclibm),这通过为每个缺省库将 -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 以及如何动态链接 libmlibc


example% CC test.c -xnolib -lCstd -Bstatic -lCrun– Bdynamic -lm -lc

-l 选项的顺序很重要。– lCstd– lCrun-lm 选项位于 -lc 之前。


注 –

建议不要静态链接 libCrunlibCstd。而是生成 /usr/lib 中的动态版本以与其所安装在的 Solaris 版本一起使用。


有些 CC 选项链接到其他库。也可以使用 -xnolib 抑制这些库链接。例如,使用 -mt 选项会导致 CC 驱动程序将 -lthread 传递给 ld。但如果同时使用 –mt–xnolibCC 驱动程序不会将 -lthread 传递给 ld。有关更多信息,请参见A.2.153 –xnolib。有关 ld 的更多信息,请参见《链接程序和库指南》。


注 –

/lib/usr/lib 中静态版本的 Solaris 库不再可用。例如,试图静态链接 libc 的操作将失败:


      CC hello.cc -xnolib -lCrun -lCstd -Bstatic -lc 

11.6 使用共享库

C++ 编译器附带下列 C++ 运行时共享库:

在 Linux 上,C++ 编译器附带这些附加库:

在 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

11.7 替换 C++ 标准库

替换与编译器一起发布的标准库是有风险的,不能保证产生预期的结果。基本操作是禁用编译器提供的标准头文件和库,指定找到新的头文件和库(及库本身的名称)的目录。

编译器支持标准库的 STLport 和 Apache stdcxx 实现。有关更多信息,请参见12.3 STLport12.4 Apache stdcxx 标准库

11.7.1 可以替换的内容

可以替换大多数标准库及其关联头文件。替换的库是 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

11.7.2 不可替换的内容

标准头文件 <exception><new><typeinfo> 与编译器本身以及 libCrun 紧密相关,不能可靠替换。库 libCrun 包含了编译器依赖且不能替换的许多“帮助”函数。

从 C 继承的 17 个标准头文件(<stdlib.h><stdio.h><string.h> 等)与 Solaris 操作系统和 Solaris 基本运行时库 libc 紧密相关,不能可靠替换。这些头文件的 C++ 版本(<cstdlib><cstdio><cstring> 等)与基本 C 版本紧密相关,不能可靠替换。

11.7.3 安装替换库

要安装替换库,必须先确定替换头文件的位置和 libCstd 的替换库。为方便讨论,假定头文件放置在 /opt/mycstd/include 中,库放置在 /opt/mycstd/lib 中。假定库称为 libmyCstd.a。(库名最好以 "lib" 开头。)

11.7.4 使用替换库

每次编译时,都使用 -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

11.7.5 标准头文件实现

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)的文件。这种明显实现有以下缺点:

为了解决这些问题,编译器 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.1 替换标准 C++ 头文件

由于11.7.5 标准头文件实现中介绍的搜索算法,您无需提供11.7.3 安装替换库中介绍的 SUNWCCh 版本的替换头文件。但是会遇到某些上文所述的问题。如果这样,建议为每个无后缀的头文件添加后缀为 .SUNWCCh 的符号链接。也就是说,对于文件 utility,可以运行命令


example% ln -s utility utility.SUNWCCh

编译器第一次查找 utility.SUNWCCh 时,会找到它,而不会和其他名为 utility 的文件或目录混淆。

11.7.5.2 替换标准 C 头文件

不支持替换标准 C 头文件。如果仍然希望提供标准头文件的自己的版本,那么建议按以下步骤操作:

例如,假设有 <stdio.h><cstdio> 的替换。请将文件 stdio.hcstdio 放在目录 /myproject/myhdr 中。在该目录中,运行如下命令:


example% ln -s stdio.h stdio.h.SUNWCCh
example% ln -s cstdio cstdio.SUNWCCh

每次编译时使用 -I/myproject/mydir 选项。

忠告:

第 12 章 使用 C++ 标准库

当在缺省(标准)模式下编译时,编译器可以访问 C++ 标准指定的整个库。库组件包括一般称为标准模板库 (Standard Template Library, STL) 的库和下列组件。

术语 STL 没有正式的定义,但是通常理解为包括容器、迭代器以及算法。以下标准库头的子集可以认为包含了 STL。

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 C++ 标准库头文件

表 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 C++ 标准库手册页

表 12–2 列出了标准库中每个组件的可用文档。

表 12–2 C++ 标准库手册页

手册页 

概述 

Algorithms

在容器和序列上执行各种操作的通用算法 

Associative_Containers

排序的容器 

Bidirectional_Iterators

可以读取和写入,并且可以双向遍历容器的迭代器 

Containers

标准模板库 (standard template library, STL) 集合 

Forward_Iterators

可以读取和写入的前移式迭代器 

Function_Objects

定义了 operator() 的对象

Heap_Operations

请参见与 make_heappop_heappush_heapsort_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

将输入或者输出序列与任意字符序列关联 

12.3 STLport

如果要使用替换 libCstd 的标准库,请使用标准库的 STLport 实现。可以使用以下编译器选项关闭 libCstd 并改用 STLport 库:

有关更多信息,请参见A.2.49 -library=l[ ,l...]

本发行版包括称为 libstlport.a 的静态归档文件和称为 libstlport.so 的动态库。

决定是否使用 STLport 实现之前,请先考虑以下信息:

12.3.1 重新分发和支持的 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> 包含在源代码中。

12.4 Apache stdcxx 标准库

通过使用 -library=stdcxx4 进行编译,在 Solaris 中使用 Apache stdcxx 版本 4 C++ 标准库代替缺省的 libCstd。此选项还会隐式设置 -mt 选项。stdcxx 库需要使用多线程模式。必须在整个应用程序的每个编译和链接命令中一致使用此选项。用 -library=stdcxx4 编译的代码与用缺省的 -library=Cstd 或可选的 -library=stlport4 编译的代码不能用在同一程序中。

第 13 章 使用传统 iostream

与 C 类似,C++ 没有内建输入或输出语句。相反,I/O 工具是由库提供的。C++ 编译器提供了 iostream 类的传统实现和 ISO 标准实现。

本章介绍了传统 iostream 库并提供了其使用示例,但并未完整介绍 iostream 库。有关更多详细信息,请参见 iostream 库手册页。要访问传统 iostream 手册页,请键入:man -s 3CC4name

13.1 预定义的 iostream

有四个预定义的 iostream

除了 cerr 之外,所有预定义的 iostream 都是完全缓冲的。请参见13.3.1 使用 iostream 进行输出13.3.2 使用 iostream 进行输入

13.2 iostream 交互的基本结构

通过将 iostream 库包括进来,程序可以使用许多输入流或输出流。每个流都具有某些源或接收器,如下所示:

流可以被限定到输入或输出,或同时具有输入和输出。iostream 库使用两个处理层来实现这些流。

标准输入、输出和错误由从类 istreamostream 派生的特殊类对象处理。

分别从 istreamostreamiostream 派生的 ifstreamofstreamfstream 类用于处理文件的输入和输出。

分别从 istreamostreamiostream 派生的 istrstreamostrstreamstrstream 类用于处理字符数组的输入和输出。

打开输入或输出流时,要创建其中一种类型的对象,并将流的 streambuf 成员与设备或文件关联。通常通过流构造函数执行此关联,因此不用直接使用 streambufiostream 库为标准输入、标准输出和错误输出预定义了流对象,因此不必为这些流创建自己的对象。

可以使用运算符或 iostream 成员函数将数据插入流(输出)或从流(输入)提取数据,以及控制插入或提取的数据的格式。

如果要插入和提取新的数据类型(其中一个类),通常需要重载插入和提取运算符。

13.3 使用传统 iostream

要使用来自传统 iostream 库的例程,必须针对所需的库部分将头文件包括进来。下表对头文件进行了具体描述。

表 13–1 iostream 例程头文件

头文件 

说明  

iostream.h

声明 iostream 库的基本功能。

fstream.h

声明文件专用的 iostreamstreambuf。包括了 iostream.h

strstream.h

声明字符数组专用的 iostreamstreambuf。包括了 iostream.h

iomanip.h

声明操纵符:iostream 中插入或提取的值有不同的作用。包括了 iostream.h

stdiostream.h

(已过时)声明 stdio 文件专用的 iostreamstreambuf。包括了 iostream.h

stream.h

(已过时)包括了 iostream.hfstream.hiomanip.hstdiostream.h。用于兼容 C++ 1.2 版的旧式流。

通常程序中不需要所有这些头文件,而仅包括所需声明的头文件。在兼容模式 (-compat[=4]) 下,传统 iostream 库是 libC 的一部分,它由 CC 驱动程序自动链接。在标准模式(缺省模式)下, libiostream 包含传统 iostream 库。

13.3.1 使用 iostream 进行输出

使用 iostream 进行的输出通常依赖于重载的左移运算符 (<<)(在 iostream 上下文中,称为插入运算符)。要将值输出到标准输出,应将值插入预定义的输出流 cout 中。例如,要将给定值 someValue 发送到标准输出,可以使用以下语句:


cout << someValue;

对于所有内置类型,都会重载插入运算符,且 someValue 表示的值转换为其适当的输出表示形式。例如,如果 someValuefloat<< 运算符会将该值转换为带小数点的适当数字序列。此处是将 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

13.3.1.1 定义自己的插入运算符

以下示例定义了 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,这样就可以在一个语句中合并多个插入。

13.3.1.2 处理输出错误

通常情况下,重载 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 中声明:


enum io_state {goodbit=0, eofbit=1, failbit=2,
badbit=4, hardfail=0x80};

有关错误函数的详细信息,请参见 iostream 手册页。

13.3.1.3 刷新

与大多数 I/O 库一样,iostream 通常会累积输出并将其发送到较大且效率通常较高的块中。如果要刷新缓冲区,只要插入特殊值 flush。例如:


cout << "This needs to get out immediately." << flush;
 

flush 是一种称为操纵符的对象示例,它是一个值,可以插入 iostream 中以起到一定作用,而不是使输出其值。它实际上是一个函数,采用 ostream&istream& 参数,在对其执行某些操作后返回其参数(请参见13.7 操纵符)。

13.3.1.4 二进制输出

要获得原始二进制形式的值输出,请使用以下示例所示的成员函数 write。该示例显示了原始二进制形式的 x 输出。


cout.write((char*)&x, sizeof(x));

以上示例将 &x 转换为 char*,这违反了类型规程。一般情况下,这样做无关大碍,但如果 x 的类型是具有指针、虚拟成员函数的类或是需要 nontrivial 构造函数操作的类,就无法正确读回以上示例写入的值。

13.3.2 使用 iostream 进行输入

使用 iostream 进行的输入类似于输出。需要使用提取运算符 >>,可以像插入操作那样将提取操作串接在一起。例如:


cin >> a >> b;

该语句从标准输入获得两个值。与其他重载运算符一样,所用的提取器取决于 ab 的类型(如果 ab 的类型不同,则使用两个不同的提取器)。ios(3CC4) 手册页中详细讨论了输入格式以及如何控制输入格式。通常,前导空白字符(空格、换行符、标签、换页等)被忽略。

13.3.3 定义自己的提取运算符

要输入新的类型时,如同重载输出的插入运算符,请重载输入的提取运算符。

string 定义了其提取运算符,如以下代码示例所示:


示例 13–1 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),将其存储在第二个参数(始终是引用),然后返回第一个参数。因为提取器会将输入值存储在第二个参数中,所以第二个参数必须是引用。

13.3.4 使用 char* 提取器

此处提及这个预定义的提取器是因为它可能产生问题。使用方法如下:


char x[50];
cin >> x;

该提取器跳过前导空白,提取字符并将其复制到 x 中,直至遇到另一个空白字符。最后完成具有终止空 (0) 字符的字符串。因为输入会溢出给定的数组,所以要小心操作。

您还必须确保指针指向了分配的存储。例如,下面列出了一个常见的错误:


char * p; // not initialized
cin >> p;

因为没有告知存储输入数据的位置,所以会导致程序的终止。

13.3.5 读取任何单一字符

除了使用 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

注 –

与其他提取器不同,char 提取器不会跳过前导空白


以下方法可以只跳过空格,并在制表符、换行符或任何其他字符处停止:


int a;
do {
    a = cin.get();
   }
while(a ==’ ’);

13.3.6 二进制输入

如果需要读取二进制值(如使用成员函数 write 写入的值),可以使用 read 成员函数。以下示例说明了如何使用 read 成员函数输入原始二进制形式的 x,这是先前使用 write 的示例的反向操作。


cin.read((char*)&x, sizeof(x));

13.3.7 查看输入

可以使用 peek 成员函数查看流中的下一个字符,而不必提取该字符。例如:


if (cin.peek()!= c) return 0;

13.3.8 提取空白

缺省情况下,iostream 提取器会跳过前导空白。可以关闭 跳过标志防止这种情况发生。以下示例先关闭了 cin 跳过空白功能,然后将其打开:


cin.unsetf(ios::skipws); // turn off whitespace skipping
...
cin.setf(ios::skipws); // turn it on again

可以使用 iostream 操纵符 wsiostream 中删除前导空白,不论是否启用了跳过功能。以下示例说明了如何从 iostream istr 中删除前导空白:


istr >> ws;

13.3.9 处理输入错误

按照约定,第一个参数为非零错误状态的提取器不能从输入流提取任何数据,且不能清除任何错误位。失败的提取器至少应该设置一个错误位。

对于输出错误,您应该定期检查错误状态,并在发现非零状态时采取某些操作(诸如终止)。! 运算符测试 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 具有可用于错误处理的成员函数。详细信息请参见手册页。

13.3.10 结合使用 iostreamstdio

可以将 stdio 用于 C++ 程序,但在程序内的同一标准流中混合使用 iostreamstdio 时,可能会发生问题。例如,如果同时向 stdoutcout 写入,会发生独立缓冲,并产生不可预料的结果。如果从 stdincin 两者进行输入,问题会更加严重,因为独立缓冲可能会破坏输入。

要消除标准输入、标准输出和标准错误中的这种问题,就请在执行输入或输出前使用以下指令:它将所有预定义的 iostream 与相应的预定义 stdio 文件连接起来。


ios::sync_with_stdio();

因为在预定义流作为连接的一部分成为无缓冲流时,性能会显著下降,所以该连接不是缺省连接。可以在应用于不同文件的同一程序中同时使用 stdioiostream。也就是说,可以使用 stdio 例程向 stdout 写入,并向连接到 iostream 的其他文件写入。可以打开 stdio 文件进行输入,也可以从 cin 读取,只要不同时尝试从 stdin 读取即可。

13.4 创建 iostream

要读取或写入不是预定义的 iostream 的流,需要创建自己的 iostream。通常,这意味着创建 iostream 库中所定义类型的对象。本节讨论了可用的各种类型:

13.4.1 使用类 fstream 处理文件

处理文件类似于处理标准输入和标准输出;类 ifstreamofstreamfstream 分别从类 istreamostreamiostream 派生而来。作为派生的类,它们继承了插入和提取运算符(以及其他成员函数),还有与文件一起使用的成员和构造函数。

可将文件 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);

该代码:

13.4.1.1 打开模式

该模式由枚举类型 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);

13.4.1.2 在未指定文件的情况下声明 fstream

可以在未指定文件的情况下声明 fstream,并在以后打开该文件。例如,以下代码创建了 ofstream toFile,以便进行写入。


ofstream toFile;
toFile.open(argv[1], ios::out);

13.4.1.3 打开和关闭文件

可以关闭 fstream,然后使用另一文件打开它。例如,要在命令行上处理提供的文件列表:


ifstream infile;
for (char** f = &argv[1]; *f; ++f) {
   infile.open(*f, ios::in);
   ...;
   infile.close();
}

13.4.1.4 使用文件描述符打开文件

如果了解文件描述符(如整数 1 表示标准输出),可以使用如下代码打开它:


ofstream outfile;
outfile.attach(1);

如果通过向 fstream 构造函数之一提供文件名或使用 open 函数来打开文件,则在销毁 fstream(通过 delete 销毁或其超出作用域)时,会自动关闭该文件。将文件 attachfstream 时,不会自动关闭该文件。

13.4.1.5 在文件内重新定位

您可以在文件中改变读取和写入的位置。有多个工具可以达到这个目的。


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 值。


13.5 iostream 赋值

iostream 不允许将一个流赋值给另一个流。

复制流对象的问题是有两个可以独立更改的状态信息版本,例如输出文件中指向当前写入位置的指针。因此,某些问题会发生。

13.6 格式控制

ios(3CC4) 手册页中详细讨论了格式控制。

13.7 操纵符

操纵符是可以在 iostream 中插入或提取以起到特殊作用的值。

参数化操纵符是具有一个或多个参数的操纵符。

因为操纵符是普通的标识符,因此会用完可能的名称,而 iostream 不会为每个可能的函数定义操纵符。本章的其他部分讨论了各种操纵符和成员函数。

有 13 个预定义的操纵符,如表 13–2 中所述。使用该表时,要进行以下假设:

表 13–2 iostream 的预定义操纵符

 

预定义的操纵符  

说明  

ostr << dec、istr >> dec

以 10 为基数进行整数转换。

ostr << endl

插入一个换行符 (’\n’) 并调用 ostream::flush()

ostr << ends

插入一个空 (0) 字符。这在处理 strstream 时很有用。

ostr << flush

调用 ostream::flush()

ostr << hex、istr >> hex

以 16 为基数进行整数转换。

ostr << oct、istr >> oct

以 8 为基数进行整数转换。

istr >> ws

提取空白字符(跳过空白),直至找到非空白字符(留在 istr 中)。

ostr << setbase(n)、istr >> setbase(n)

将转换基数设置为 n(仅限 0、8、10、16)。

ostr << setw(n)、istr >> setw(n)

调用 ios::width(n)。将字段宽度设置为 n。

10 

ostr << resetiosflags(i)、istr >> resetiosflags(i) 

根据 i 中设置的位,清除标志位向量。

11 

ostr << setiosflags(i)、istr >> setiosflags(i)

根据 i 中设置的位,设置标志位向量。

12 

ostr << setfill(c)、istr >> setfill(c)

将填充字符(用来填充字段)设置为 c

13 

ostr << setprecision(n)、istr >> setprecision(n)

将浮点精度设置为 n 位数。

要使用预定义的操纵符,必须在程序中包含文件 iomanip.h

您可以定义自己的操纵符。操纵符共有两个基本类型:

13.7.1 使用无格式操纵符

无格式操纵符是具有如下功能的函数:

由于为 iostream 预定义了采用(指向)此类函数(的指针)的移位运算符,因此可以在输入或输出运算符序列中放入函数。移位运算符会调用函数而不是尝试读取或写入值。例如,下面是将 tab 插入 ostreamtab 操纵符:


ostream& tab(ostream& os) {
             return os <<’\t’;
            }
...
cout << x << tab << y;

详细描述实现以下操作的方法:


const char tab = ’\t’;
...
cout << x << tab << y;

下面示例显示了无法用简单常量来实现的代码。假设要对输入流打开或关闭空白跳过功能。可以分别调用 ios::setfios::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;
}

13.7.2 参数化操纵符

iomanip.h 中包含的其中一个参数化操纵符是 setfillsetfill 设置用于填写字段宽度的字符。该操作按照下例所示实现:


//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);
}

参数化操纵符的实现分为两部分:

头文件 iomanip.h 中定义了多个类。每个类都保存一个操纵符函数的地址和一个参数的值。manip(3CC4) 手册页中介绍了 iomanip 类。上面的示例使用了 smanip_int 类,它是与 ios 一起使用。因为该类与 ios 一起使用,所以也可以与 istreamostream 一起使用。上面的示例还使用了另一个类型为 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);
   }

13.8 用于数组的 strstream: iostream

请参见 strstream(3CC4) 手册页。

13.9 用于 stdio 文件的 stdiobuf: iostream

请参见 stdiobuf(3CC4) 手册页。

13.10 streambuf

iostream 是由两部分(输入或输出)构成的系统的格式化部分。系统的其他部分由处理无格式字符流的输入或输出的 streambuf 组成。

通常,可以通过 iostream 使用 streambuf,因此,不必担心 streambuf 的细节。您可以选择直接使用 streambuf,例如,如果需要提高效率或解决 iostream 内置的处理或格式化错误。

13.10.1 streambuf 工作方式

streambuf 由字符流或字符序列和一个或两个指向相应序列的指针组成。每个指针都指向两个字符间。(实际上,指针无法指向字符间,但可以按这种方式考虑指针。)有两种 streambuf 指针:

streambuf 可以有其中一个指针,也可以两个全有。

13.10.1.1 指针位置

可以使用多种方法来操作指针的位置和序列的内容。操作两个指针时它们是否都会移动取决于使用 streambuf 种类。通常,如果使用队列式 streambuf,get 和 put 指针独立移动;如果使用文件式 streambuf,get 和 put 指针总是一起移动。例如,strstream 是队列式流,fstream 是文件式流。

13.10.2 使用 streambuf

从来不创建实际的 streambuf 对象,而是只创建从 streambuf 类派生的类的对象。例如 filebufstrstreambuffilebuf(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();

然后源文件被提取到目标中。两种方法是完全等同的。

13.11 iostream 手册页

许多 C++ 手册页都介绍了 iostream 库的详细信息。下表概述了每个手册页中的内容。

要访问传统 iostream 库手册页,请键入:


example% man -s 3CC4 name
表 13–3 iostream 手册页概述

手册页 

概述  

filebuf

详细介绍了从 streambuf 派生并专用于文件的类 filebuf 的公用接口。有关从类 streambuf 继承的功能的详细信息,请参见 sbufpub(3CC4) 和 sbufprot(3CC4) 手册页。可通过类 fstream 使用 filebuf 类。

fstream

详细介绍了类 ifstreamofstreamfstream 的专用成员函数,这些类是用于文件的 istreamostreamiostream 专用版本。

ios

详细介绍了作为 iostream 的基类的类 ios 的各个部分。该类也包含了所有流公共的状态数据。

ios.intro

简要介绍了 iostream

istream

详细说明了以下内容: 

  • istream 的成员函数,这些函数支持对从 streambuf 获取的字符进行解释

  • 输入格式化

  • 归为类 ostream 的一部分的定位函数

  • 某些相关函数

  • 相关操纵符

manip

介绍了 iostream 库中定义的输入和输出操纵符。

ostream

详细说明了以下内容: 

  • ostream 的成员函数,这些函数支持对写入 streambuf 的字符进行解释

  • 输出格式化

  • 归为类 ostream 的一部分的定位函数

  • 某些相关函数

  • 相关操纵符

sbufprot

介绍了对从类 streambuf 派生的类进行编码的程序员所需的接口。有关 sbufprot(3CC4) 手册页中未讨论的一些公用函数,另请参见 sbufpub(3CC4) 手册页。

sbufpub

详细介绍了 streambuf 类的公用接口,尤其是 streambuf 的公用成员函数。该手册页包含了直接处理 streambuf 类型的对象所需的信息,或是查找从 streambuf 派生的类从其继承的函数所需的信息。如果要从 streambuf 派生类,另请参见 sbufprot(3CC4) 手册页。

ssbuf

详细介绍了从 streambuf 派生并专用于处理字符数组的类 strstreambuf 的专用公用接口。有关从类 streambuf 继承的功能的详细信息,请参见 sbufpub(3CC4) 手册页。

stdiobuf

简要介绍了从 streambuf 派生并专用于处理 stdio 文件的 stdiobuf 类。有关从 streambuf 类继承的功能的详细信息,请参见 sbufpub(3CC4) 手册页。

strstream

详细介绍了由从 iostream 派生并专用于处理字符数组的一组类实现的 strstream 的专用成员函数。

13.12 iostream 术语

iostream 库说明中常常使用一些与一般编程中的术语类似的术语,但有特殊含义。下表定义了讨论 iostream 库时使用的这些术语。

表 13–4 iostream 术语

iostream 术语

定义  

缓冲区

该词有两个含义,一个特定于 iostream 软件包,另一个较常适用于输入和输出。

iostream 库特定相关时,缓冲区是由类 streambuf 定义的类型的对象。

通常,缓冲区是一个内存块,用于将字符高效传输到输出的输入。对于已缓冲的 I/O,缓冲区已满或被强制刷新之前,字符的实际传输会延迟。 

无缓冲的缓冲区是指在其中没有上文定义的通用意义的缓冲区的 streambuf。本章避免使用缓冲区一词来指 streambuf。但是,手册页和其他 C++ 文档使用缓冲区一词来表示 streambuf

提取

iostream 获取输入的过程。

Fstream

专用于文件的输入或输出流。特指以 courier 字体输出时从类 iostream 派生的类。

插入

将输出发送到 iostream 中的过程。

iostream

通常为输入或输出流。 

iostream

通过 include 文件 iostream.hfstream.hstrstream.hiomanip.hstdiostream.h 实现的库。因为 iostream 是面向对象的库,所以应扩展该库。因此,可以对 iostream 库执行的某些操作并未实现。

通常是指 iostreamfstreamstrstream 或用户定义的流。

Streambuf

包含字符序列的缓冲区,其中字符具有 put 或 get 指针(或兼有)。以 courier 字体输出时,它表示特定类。否则,通常是指 streambuf 类或从 streambuf 派生的类的对象。任何流对象都包含从 streambuf 派生的类型的对象或指向对象的指针。

Strstream

专用于字符数组的 iostream。它是指以 courier 字体输出时的特定类。

第 14 章 使用复数运算库

复数是由部和部组成的数。例如:

3.2 + 4i 1 + 3i 1 + 2.3i

在特例情况下,如 0 + 3i 是纯虚数,通常写为 3i5 + 0i 是纯实数,通常写为 5。可以使用 complex 数据类型来表示复数。


注 –

复数运算库 (libcomplex) 仅可用于兼容模式 ( -compat[ =4])。在标准模式(缺省模式)下,C++ 标准库 libCstd 附带具有类似功能的复数类。


14.1 复数库

复数运算库以新的数据类型实现复数数据类型,并提供:

复数也可表示为绝对值(或幅度)和参数(或角度)。该库提供了在实部虚部(笛卡尔)表示形式和幅度角度(极)表示形式之间进行转换的函数。

数字复共轭的虚部中有相反符号。

14.1.1 使用复数库

要使用复数库,应在程序中包含头文件 complex.h,并使用 -library=complex 选项进行编译和链接。

14.2 complex 类型

复数运算库定义了一个类:complex 类。complex 类的对象可以存放一个复数。复数由两部分构成:


class complex {
    double re, im;
};

complex 类的对象值是一对 double 值。第一个值表示实部,第二个值表示虚部

14.2.1 complex 类的构造函数

有两个用于 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 类型的析构函数。

14.2.2 算术运算符

复数运算库定义了所有基本的算术运算符。具体来说,以下运算符按一般方法和普通的优先级工作:

+ - / * =

减法运算符 (-) 具有其通常的二元和一元含义。

此外,您可以按通常的方法使用以下运算符:

但是,若将以上四个运算符用于表达式,则不产生任何值。例如,下列表达式无法进行运算:


complex a, b;
...
if ((a+=2)==0) {...}; // illegal
b = a *= b; // illegal

另外还可以使用等号 (==) 和不等号 (!=),它们具有常规含义。

将运算表达式中的实数和复数混合时,C++ 使用复数运算符函数并将实数转换为复数。

14.3 数学函数

复数运算库提供了许多数学函数。一些是专用于复数的,而其余的则是标准 C 数学库中函数的复数版本。

全部这些函数为每个可能的参数产生结果。如果函数无法生成具有数学意义的结果,它就调用 complex_error 并返回适用的某值。具体来说,这些函数会尽量避免实际的溢出,而是调用 complex_error 并显示消息。下表描述了复数运算库函数的提示。


注 –

sqrtatan2 函数的实现遵循 C99 csqrt 附录 G 规范。


表 14–1 复数运算库函数

复数运算库函数  

说明 

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 ).它计算出 bexp 次幂。

complex sin(const complex)

返回其参数的正弦。 

complex sinh(const complex)

返回其参数的双曲正弦。 

complex sqrt(const complex)

返回其参数的平方根。 

complex tan(const complex)

返回其参数的正切。 

complex tanh(const complex)

返回其参数的双曲正切。 

14.4 错误处理

复数库具有以下用于错误处理的定义:


extern int errno;
class c_exception {...};
int complex_error(c_exception&);

外部变量 errno 是来自 C 库的全局错误状态。errno 可以为标准头文件 errno.h 中所列值(请参见 perror(3) 手册页)。没有任何函数会将 errno 设置为零,但有许多函数会将它设置为其他值。

要分辨特定运算是否失败:

  1. 在运算前将 errno 设置为零。

  2. 测试运算。

函数 complex_error 采用对类型 c_exception 的引用并由下列复数运算库函数调用:

缺省版本的 complex_error 返回零。这个零值的返回意味着发生了缺省的错误处理。可以提供自己的替换函数 complex_error,以执行其他错误处理。cplxerr(3CC4) 手册页中介绍了错误处理。

cplxtrig(3CC4) 和 cplxexp(3CC4) 手册页中介绍了缺省的错误处理,下表中也进行了简要介绍。

复数运算库函数  

缺省错误处理汇总  

exp

如果产生溢出,将 errno 设置为 ERANGE,并返回一个极大的复数。

log、log10

如果参数为零,将 errno 设置为 EDOM,并返回一个极大的复数。

sinh、cosh

如果参数的虚部产生溢出,则返回一个零复数。如果实部产生溢出,则返回一个极大的复数。无论是以上哪种情况,都将 errno 设置为 ERANGE

14.5 输入和输出

复数运算库提供了用于复数的缺省提取器插入器,如以下示例所示:


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

14.6 混合模式运算

类型 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)

14.7 效率

设计 complex 类主要为了提高效率。

最简单的函数声明为 inline,以消除函数调用开销。

在函数不同时就会提供函数的多个开销版本。例如,pow 函数有多个版本,分别取类型为 doubleint 以及 complex 的指数,而前者的运算简单得多。

在包含 complex.h 时,会自动包含标准 C 数学库头文件 math.h。然后 C++ 开销规则就会产生类似于下面的表达式效率评估:


double x;
complex x = sqrt(x);

在此示例中,调用了标准数学函数 sqrt(double),且结果转换为 complex 类型,而不是先转换为 complex 类型再调用 sqrt(complex)。该结果转向重载决策规则的外部,正好是您所希望的结果。

14.8 复数手册页

复数运算库的剩余文档由下表所列的手册页组成:

表 14–3 有关 complex 类型的手册页

手册页 

概述  

cplx.intro(3CC4)

对复数运算库的一般性介绍 

cartpol(3CC4)

笛卡尔函数和极函数 

cplxerr(3CC4)

错误处理函数 

cplxexp(3CC4)

指数、对数和平方根函数 

cplxops(3CC4)

算术运算符函数 

cplxtrig(3CC4)

三角函数 

第 15 章 生成库

本章解释了如何生成您自己的库。

15.1 认识库

库具有两点好处。首先,它们提供了在多个应用程序间共享代码的方法。如果您有要共享的代码,则可以创建一个具有该代码的库,并将该库链接到需要这些代码的应用程序。其次,库提供了降低大型应用程序复杂性的方法。这类应用程序可以将相对独立的部分生成为库并进行维护,因此减轻程序员在其他部分工作的负担。

生成库只不过是创建 .o 文件(使用 -c 选项编译代码)并使用 CC 命令将 .o 文件并入库中。可以生成两种库:静态(归档)库和动态(共享)库。

对于静态(归档)库,库中的对象在链接时链接到程序的可执行文件中。只有库中属于应用程序所需的那些 .o 文件链接到可执行文件。静态(归档)库名称通常以 .a 后缀结尾。

对于动态(共享)库,库中的对象并不链接到程序的可执行文件,而是链接程序在可执行文件中注明程序依赖于库。执行该程序时,系统会装入程序所需的动态库。如果使用同一动态库的两个程序同时执行,那么操作系统在程序间共享这个动态库。动态(共享)库名称以 .so 后缀结尾。

动态链接共享库较静态链接归档库有多个优势:

但动态库也具有一些缺点:

15.2 生成静态(归档)库

生成静态(归档)库的机制与生成可执行文件相似。可以使用 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 文件中有一个函数可以确保将只从归档文件链接应用程序所需的那些符号。

15.3 生成动态(共享)库

动态(共享)库的生成方式与静态(归档)库的生成方式基本相同,除了在命令行上使用 –G 而不是 –xar

不应直接使用 ld。与静态库一样,CC 命令可以确保使用模板时,模板系统信息库中所有必要的模板实例都包括在库中。在执行 main() 之前会调用与应用程序链接的动态库中所有静态构造函数,在 main() 退出之后会调用所有静态析构函数。如果使用 dlopen() 打开共享库,所有静态构造函数都在执行 dlopen() 时执行,所有静态析构函数都在执行 dlclose() 时执行。

应该使用 CC -G 来生成动态库。使用 ld(链接编辑器)或 cc(C 编译器)生成动态库时,异常可能无法生效,且库中定义的全局变量未初始化。

要生成动态(共享)库,必须使用 CC–Kpic–KPIC 选项编译每个对象来创建可重定位的目标文件。然后您就可以生成一个具有这些可重定位目标文件的动态库。如果遇到异常的链接失败,可能是忘记了使用 –Kpic–KPIC 编译某些对象。

要生成名为 libfoo.so 的 C++ 动态库(该库包含源文件 lsrc1.cclsrc2.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

15.4 生成包含异常的共享库

对于包含 C++ 代码的程序,切勿使用 -Bsymbolic,而应使用链接程序映射文件。如果使用 -Bsymbolic,不同模块中的引用会绑定到应是一个全局对象内容的不同副本。

异常机制依赖对地址的比较。如果您具有某项内容的两个副本,它们的地址就不等同且异常机制可能失败,这是由于异常机制依赖对假设为唯一地址内容的比较。

15.5 生成专用的库

在组织生成一个仅供内部使用的库时,可以使用不建议在一般情况下使用的选项来生成这个库。具体来说,库不需要符合系统的应用程序二进制接口 (application binary interface, ABI)。例如,可以使用 -fast 选项编译库,以提高其在某已知体系结构上的性能。同样,可以使用 -xregs=float 选项编译库以提高性能。

15.6 生成公用的库

在组织生成一个供其他公司使用的库时,库的管理、平台的一般性以及其他问题就变得尤为重要。一个用于检验库是否为公用的简单测试就是询问应用程序程序员是否可以轻松地重新编译该库。生成公用库时应该符合系统的应用程序二进制接口 (application binary interface, ABI)。通常,这意味着应该避免任何特定于处理器的选项。(例如,不使用 –fast–xtarget。)

SPARC ABI 为应用程序保留了一些专用寄存器。对于 V7 和 V8,这些寄存器是 %g2%g3%g4。对于 V9,这些寄存器是 %g2%g3。由于多数编译用于应用程序,所以在缺省情况下,为了提高程序的性能,C++ 编译器将这些寄存器作为临时寄存器使用。但是,对公用库中寄存器的使用通常不兼容于 SPARC ABI。生成公用库时,请使用 -xregs=no%appl 选项编译所有对象,以确保不会使用应用程序寄存器。

15.7 生成具有 C API 的库

如果要生成以 C++ 编写但可用于 C 程序的库,必须创建 C API(application programming interface,应用程序编程接口)。为此,应先使所有导出的函数为 extern "C"。注意,只有在全局函数中才能够完成该操作,在成员函数中不行。

如果 C 接口库需要 C++ 运行时支持,且要使用 cc 进行链接,则在使用 C 接口库时,还必须用 libC(兼容模式)或 libCrun(标准模式)链接应用程序。(如果 C 接口库不需要 C++ 运行时支持,就不必用 libClibCrun 进行链接。)归档库与共享库的链接步骤是不同的。

提供归档的 C 接口库时,必须提供如何使用该库的说明。

提供共享的 C 接口库时,必须在生成库时创建对 libClibCrun 的依赖性。如果共享库具有正确的依赖性,就不必在使用该库时将 -lC-lCrun 添加到命令行。

如果要删除对 C++ 运行时库的任何依赖性 ,应该在库源文件中强制应用下列代码规则:

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

如果要使用 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 程序可能会出现无规律的行为。