本篇博客笔记顺序大体按照《C++标准程序库(第1版)》各章节顺序编排。

--------------------------------------------------------------------------------------------

13 以Stream Classes 完成输入和输出 13.1 String对象

(1)C++ I/O 由streams完成。所谓stream就是一条数据“流”。输出操作被解读为“数据流入stream”,输入操作则是“数据流出stream”。

(2)全局性的Stream对象

1. cin(隶属于istream), 标准输入通道;

2. cout(隶属于ostream),标准输出通道;

3. cerr(隶属于ostream),标准错误输出通道;

4. clog(隶属于ostream),标准日志通道;

(3)Stream操作符: operator >> 和 operator << 都被相应的stream classes 重载,分别用于输入和输出。因此,C++ 移位操作符摇身一变成了I/O操作符。

(4)操控器,表13.1 列出了IOStream 程序库中一些重要的操控器。

13.2 基本的Stream类别和 Stream对象

(1)IOStream程序库中的stream classes形成了图13.1所示的阶层体系。

解析如下:

1. 基类 ios_base 定义了stream classes 的所有“与字符型别及其相应之字符特性(traits)无关”的属性,主要包含状态和格式标志等组件和函数。

2. 由 ios_base 派生的 template class basic_ios<>,定义出“与字符型及其相关之字符特性(traits)相关”的 stream classes 共同属性,其中包括 stream 所用的缓冲器。缓冲器所属型别派生自 template class basic_streambuf<>,其具现参数和 basic_ios<> 一致。basic_streambuf<> 负责实际的读写操作。

3. template class basic_istream<> 和 basic_ostream<> 两者都虚拟继承自basic_ios<>,分别定义出用于读/写的对象。和 basic_ios<>一样,它们以字符型别及其特性(traits)作为参数。如果无关乎国际化议题,一般使用由字符型别char体现出来的istream和ostream就够了。

4. template class basic_iostream<> 派生(多重继承)自 basic_istream<> 和 basic_ostream<> ,用来定义既可读取亦可改写的对象。

5. template class basic_streambuf<> 是IOStream程序库的核心,定义出所有“可改写的stream”或“可读取的stream”的接口。其它stream classes 均利用它进行实际的字符读写操作。程序中为处理某些外部表达,必须从basic_streambuf<> 派生一些可用类别。

IOStream 程序库严格依照“职责分离”的原则来设计。basic_ios派生类别只处理数据的格式化,实际读写操作由basic_ios派生类别所维护的stream buffers完成。stream buffers 提供读写时所使用的字符缓冲区,并形成对外部表述(如文件和字符串)的一种抽象概念。

运用stream buffers ,我们可以轻松定义出对于新的外部表述(例如某种新的存储设备)的存取操作。我们需要做的仅仅是从basic_streambuf<>派生出一个新的stream buffer类别(或其适当特化版本),并定义该外部表述的字符读写函数即可。如果某个stream 对象初始化时候使用了该stream buffer,则所有I/O格式化操作的选项均可自动生效。

(2)具体的类别定义 和IOStream程序库的所有template classes一样,basic_ios<>有两个参数:

namespace std
{
template <class charT,
class traits = char_traits<charT> >
class basic_ios; }

这两个参数分别是1. stream classes所使用的字符型别,2. 前者的特性类别(traits class)

在特性类别中,enf-of-file值和复制/移动字符序列的各个指令,都属于特性(traits)的一部分。字符型别的特性(traits)和字符型别本身通常密不可分,因此定义一个template class并针对特定型别实施特化也就合情合理。针对字符型别charT,特性类别缺省为char_traits<charT>。关于char_traits<>,标准程序库提供了char和wchar_t两个特化版本。下面是两个最常使用的basic_ios<>具体实体:

namespace std
{
typedef basic_ios<char> ios;
typedef basic_ios<wchar_t> wios;
}

针对basic_streambuf<>, basic_istream<>, basic_ostream<>, basic_iostream<>,当然也以字符型别和特性类别(traits class)作为参数。

针对前面提到的全局性的stream对象,同样分别以char或对应的wchar_t作为字符型别,用来存取标准I/O通道。表13.2

缺省情况下,这些stream都和标准C stream同步。也就是说C++标准程序库确保在“混合使用C++ streams 和 C streams ”的情况下,顺序有保障。任何标准C++ stream 缓冲区在改写数据前,都会先刷新其所对应的C Stream缓冲区,反之亦然。当然,保持同步会占用一定时间。如果你不需要这样的功能,可以在输入或输出前调用sync_with_stdio(false),便可取消同步。

(3)各个stream classes 的定义分散于一下数个头文件:

1. <iosfwd>:内含stream classes 的前置声明。

2. <streambuf>:内含stream buffer基类(basic_streambuf<>)的定义。

3. <istream>:内含仅支持输入的类别(basic_istream<>)和同时支持输入输出的类别(basic_iostream<>)的定义。

4. <ostream>: 内含output stream(basic_ostream<>)的类别定义。

5. <iostream>:内含全局性的stream对象(例如cin和cout)的定义。

大部分头文件主要用于C++标准程序库的内部组织。IOStream程序库使用者只需含入拥有各个stream classes 声明式的<iosfwd>,并在运用输入或输出功能时分别含入<istream>或<ostream>即可。除非用到标准stream对象,否则不需要含入<iostream>。在某些实作版本中,每一个含入<iostream>的编译单元在启动(start-up)时都需要执行一段程序代码;虽说其执行负荷并不高,但却必须加载相应的执行分页,这项耗费可能不小。一般来说,有必要含入的头文件,我们才含入它。

(4)C++ 的操作符 << 和 >> 分别用于位左移和右移,然而basic_istream<> 和 basic_ostream<> 重载了它们,使之成为标准的I/O 操作符。使用这两个操作符,我们不再需要指定待打印的型别,只要针对不同型别进行重载,就可以保证编译器会自动推断出正确的打印函数。同时它们还具有可串接打印的特性(因为返回的依然是一个stream对象)。

标准 I/O 操作符还定义了bool,char* 和 void* 型别的输入/输出。此外我们也可以更加扩展,将<< 和 >> 运用在我们自己定义的型别身上。

13.4 Streams 的状态

(1)用来表示Streams状态的一些常数 表13.3 iostate型别的常量

注意,eofbit常常和failbit同时出现,因为在end-of-file之后再试图读取数据,就会检测出end-of-file状态。读取最后一个字符时,eofbit并未设立,但再一次试图读取字符时,就会导致eofbit和failbit同时被设立,因为读取操作也失败了。

(2)用来处理Streams状态的一些成员函数 表13.4 用于处理Streams状态的各个成员函数

如果某些标志被clear() 或 setstate() 设立,streams 便有可能抛出异常。如果对应的标志被设立起来,那么当那些标志的处置函数结束之际,stream会抛出异常。注意,我们必须明白地清除错误位。C 语言可以在“格式错误”发生之后仍然读入字符。例如虽然scanf()未能读入一个整数,我们仍能读入剩余字符,因此虽然读入操作失败,input stream 的状态依然ok。但C++不同:如果设置了failbit,除非显式予以清除,否则无法进行下一个操作。

请注意,被设立的位只是反映过去曾发生的事。如果某次操作后发现某个为被设立了,我们无法确定究竟是这一次或先前操作导致这个结果。因此如果想通过标志了解错误,操作前应先设立goodbit(如果尚未设立的话)。

如,下面这个例子检查failbit 是否设立。若是,则清除之:

// check whether failbit is set
if (strm.rdstata() & std::ios::failbit)
{
std::cout << "failbit was set " << std::endl;
// clear only failbit
strm.clear(strm.rdstata() & ~std::ios::failbit);
}

(3)Stream 状态与布尔条件测试 stream 定义了两个可用于布尔表达式的函数。如表13.5 可用于布尔表达式的Stream 操作符

以上技术的一个典型应用就是以循环读入对象并处理:

// as long as obj can be read
while (std::cin >> obj)
{
// process obj (in this case, simply output it)
std::cout << obj << std::endl;
}

(4)Stream 的状态和异常 标准化之后的streams允许我们对任何一种状态标志进行定义:此一状态标志被设立时是否引发异常。这可由成员函数exceptions() 完成,如表13.6

如果调用带有唯一参数的exceptions(),那么一旦指定的那个标志被设立起来,立刻就会引发相应异常。如下:

// 下面这个例子要求stream对所有标志均抛出异常:
// throw exceptions for all "errors"
strm.exceptions(std::ios::eofbit | std::ios::failbit | std::ios::badbit);

13.5 标准I/O函数
(1)输入用的成员函数
如表13.7, Stream函数读取字符序列的性能


(2)输出用的成员函数

1. ostream& ostream::put(char c)
2. ostream& ostream::write(const char* str, streamsize count)
3. ostream& ostream::flush()

13.6 操控器(Manipulators)

如表13.8 定义于<istream> 或 <ostream> 中的操控器

操控器其实就是一个被I/O操作符调用的函数。如下:

ostream& ostream::operator << (ostream& (*op)(ostream&))
{
// call the function passed as parameter with this stream as the argument
return (*op)(*this);
} std::cout << std::endl;
// 这里的<< 分别以cout 和 end() 为操作数,从前述实作法可知,操作符 <<把它本身的
// 调用操作转换为一个以stream为参数的函数调用,直接使用下面表达式效果一样。
std::endl(std::cout)

13.7 格式化 如表13.9 所示 可以访问格式标志的成员函数

13.9 文件存取(File Access) (1)

stream 可用来存取文件。C++标志程序库提供了四个template classes,并定义了四个标准特化版本:

1. template class basic_ifstream<> 及其特化版本ifstream和wifstream,用来读取文件(是一种"input file stream")

2. template class basiic_ofstream<> 及其特化版本ofstream和wofstream,用来将数据写入文件(是一种"output file stream")

3. template class basic_fstream<> 及其特化版本fstream和wfstream,用于读写文件

4. template class basic_filebuf<> 及其特化版本filebuf和wfilebuf,被其它file stream classes 用来进行实际的字符读写工作。 这些classes和stream base classes的关系如图13.2

注意
1. file stream classes 都不以string 作为构造函数的参数型别。
2. 如果文件有可能在它被产生的范围(scope)之外被使用,我们应该从heap分配该文件对象,并且在不需要时删除之:

// 这时候就应当使用某种smart pointer class
std::ofstream* filePtr = new std::ofstream("xyz")
....
delete filePtr;

(2)文件标志 为了准确控制文件处理模式,class ios_base定义了一组标志,其型别都是openmode,这是类似fmtflags的一种位掩码型别(bit mask type),如表13.32

表13.33 展示"C++标志组合"和"C的文件开启函数fopen()所使用之接口字符串"间的关联。标志binary和ate的组合没有列出。设置binary相当于在接口字符串后加一个b,设置ate则相当于打开文件后立即跳至文件尾端。

表13.34 列出用来打开或关闭文件的一些成员函数

注意,处理过文件之后,必须调用clear() 以清除当时被设于文件尾端的状态标志。这是必要的,因为这个stream对象被多个文件共享。open()并不会清除任何状态标志,因此如果某个stream未处于良好状态,在关闭并重新打开之后,你还是必须调用clear()以取得一个良好状态。即使你透过它开启另一个文件,情况也一样。

// io/ cat1.cpp
// header files for file I/O
#include <fstream>
#include <iostream>
using namespace std; /* for all file names passed as command-line arguments
* -open, print contents, and close file
*/
int main(int argc, char* argv[])
{
ifstream file; // 注意,这个file stream 稍后将被多个文件共享
// for all command-line arguments
for (int i = ; i < argc; ++i)
{
// open file
file.open(argv[i]);
// write file contents to cout
char c;
while (file.get(c))
{
cout.put(c);
}
// clear eofbit and failbit set due to end-of-file
file.clear(); // close file
file.close();
}
}

(3)随机存取
表13.35 列出的成员函数,用来为C++ streams 确定读写位置。


表13.36 列出用于相对位置的常数

// seek to the beginning of the file
file.seekg(, std::ios::beg)
...
// seek 20 character forward
file.seekg(, std::ios::cur);
...
// seek 10 character before the end
file.seekg(-, std::ios::end); #include <iostream>
#include <fstream> void printFileTwice(const char* filename)
{
// open file
std::ifstream file(filename); // print contents the first time
std::cout << file.rdbuf(); // seek to the beginning
file.seekg(); print contents the second time
std::cout << file.rdbuf();
}

注意,file.rdbuf()被用来打印文件内容。此时是直接操作stream缓冲区,那并不会改变stream状态。如果透过stream接口函数(例如透过getline())打印file内容, 必须先调用clear()清除file的状态——在它能够被任何方式(任何函数)处理之前(包括改变读写位置),因为这些函数到达文件尾端时会设立ios::eofbit 和 ios::failbit。

(4)文件描述符(FIle Descriptors)

某些实作版本提供这种可能性:将一个stream附着到一个已开启的I/O通道。为了这么做,你必须以一个文件描述符将file stream 初始化。文件描述符是个整数,用来辨识某个开启的I/O通道。 有三个文件描述符是预先定义好的:

1. 0 代表标准输入通道

2. 1 代表标准输出通道

3. 2 代表标准错误信息通道

这些通道可能被连接至文件、控制台,其他行程(processes)或其他I/O设施。

13.10 连接Input Streams 和 Output Streams 常常会需要连接两个streams。例如你可能想在读取数据前确保屏幕上已经打印出提示文字;或者你可能希望对同一个stream读取和改写——这种情况主要发生在file stream身上。

(1)你可以把一个stream连接到一个output stream身上。这意味两者的缓冲区是同步的。 其具体作法是:output stream 将在另一个stream 执行输入或输出操作前先清空自己的缓冲区。也就是说对output stream 而言,其flush() 函数会被调用。

表13.37 列出basic_ios定义的数个成员函数,它们用来将某个stream连接到另一个stream身上。每个stream 只能连接一个output stream,但你可以把一个output stream 连接到多个streams身上。

缺省情况下,标准input装置以下列方式连接到标准output装置上:

// predefined connections
std::cin.tie(&std::cout);
std::wcin.tie(&std::wcout);

这样就保证了在真正请求输入之前,一定会先清空output缓冲区。如下:

std::cout << "Please enter x :" ;
std::cin >> x;

程序读取x之前会先隐式(implicitly)对cout调用函数flush()。
如果要删除两个stream间的连接,可传递0或NULL给tie()。例如:

// decouple cin from any output stream
std::cin.tie(static_cast<std::ostream*>());

(2)以stream缓冲区完成"紧耦合"(Tight Coupling) 透过函数rdbuf(),可以使不同的streams共享一个缓冲区,从而实作streams的紧耦合。

表13.38 所列函数使用与不同目的

rdbuf()允许数个stream对象从同一个input通道读取信息,或者对同一个output通道写入信息,而不必困扰于I/O次序。由于I/O操作被施以缓冲措施,所以同时使用多个stream缓冲区是麻烦的。因为,对着同一个I/O通道使用不同的streams,而<font size="" color="">这些streams的缓冲区又各不相同</font>,意味I/O得传递给其它IO。basic_istream和basic_ostream各有构造函数接受一个stream缓冲区作为参数,以此将stream初始化。

#include <iostream>
#include <fstream>
using namespace std; int main()
{
// stream for hexadecimal standard output
hexout.setf(ios::hex, ios::basefield);
hexout.setf(ios::showbase); // switch between decimal and hexadecimal output
hexout << "hexout :" << << " "; // 输出hexout : 0xb1
cout << "cout :" << << " "; // 输出cout : 177
hexout << "hexout :" << - << " "; // 输出hexout : 0xffffffcf
cout << "cout :" << - << " "; // 输出cout : -49
hexout << endl;
}

(3)注意,basic_stream和basic_ostream 的析构函数并不删除相应的stream缓冲区(毕竟该缓冲区并非由这些classes打开)。因此你可以传递一个指向stream缓冲区的指针,而不必使用stream reference。

#include <iostream>
#include <fstream> void hexMultiplicationTable(std::streambuf* buffer, int num)
{
std::ostream hexout(buffer);
hexout << std::hex << std::showbase;
for (int i = ; i <= num; ++i)
{
for (int j = ; j <= ; ++j)
{
hexout << i * j << ' ';
}
hexout << std::endl;
}
} int main()
{
using namespace std;
int num = ;
cout << "We print " << num << " lines hexadecimal" << endl; hexMultiplicationTable(cout.rdbuf(), num); cout << "That was the output of " << num << "hexadecimal lines" << endl;
}

这种方法的优点在于各式被修改后不必恢复其原先状态,因为格式乃是针对stream对象而不是针对stream缓冲区。缺点则是:stream对象的构造和析构会有更多额外开销。

(4)只有basic_istream和basic_ostream不销毁stream缓冲区。其它streams都会销毁它们最初分配的stream缓冲区,但它们不会销毁以rdbuf() 设置的缓冲区(stream对象--stream缓冲区)。

(5)将标准Streams重新定向(Redirectin) 只要透过"设置stream缓冲区"就可以重定向某个stream。如下:

// 使写入cout的信息不被送到标准output通道,而是被送到cout.txt
std::ofstream file("cout.txt");
std::cout.rdbuf(file.rdbuf());
// 函数copyfmt() 可用来将某个stream的所有格式信息赋值给另一个stream对象
std::ofstream file("cout.txt")
file.copyfmt(std::cout);
std::cout.rdbuf(file.rdbuf());

注意,上述file是局部对象,将在上述程序区段结束时被销毁,相应的stream缓冲区也一并被销毁。这和标准的streams不同,因为通常file streams 在构造过程分配stream缓冲区,并于析构时销毁它们。所以本例中的cout不能再被用于写入(缓冲区被一并销毁了)。事实上它甚至无法在程序结束时被安全销毁。因此我们应该保留缓冲区并于事后恢复。如下面程序:

#include <iostream>
#include <fstream>
using namespace std; int main()
{
cout << " the first row " << endl; redirect(cout); cout << " the last row" << endl;
} void redirect(ostream& strm)
{
ofstream file("redirect.txt"); // save output buffer of the stream
streambuf* strm_buffer = strm.rdbuf(); // 保留旧缓冲区 file << "one row for the file " << endl;
strm << "one row for the stream " << endl; // restore old output buffer
strm.rdbuf(strm_buffer);
}
程序输出:
the first row
the last row
文件redirect.txt的内容如下:
one row for the file
one row for the stream

(6)用于读写的Streams
运用同一个stream进行读写操作。如下:

std::fstream file("example.txt", std::ios::in | std::ios::out);

也可以采用两个不同的stream对象,一个用于读取,一个用于改写。如下:

std::ofstream out ("example.txt", ios::in | ios::out);
std::istream in(out.rdbuf());

out的声明式会开启文件。in 的声明式使用out的stream缓冲区,从中读出数据。注意out必须同时允许读取和改写,如果仅能改写,则从它身上读取数据会导致未定义行为。另外还请注意,in并非隶属ifstream型别,只是隶属istream型别——文件被打开后,就有相应的stream缓冲区,此时唯一需要的只是另一个stream对象。

13.11 String Stream Classes

Stream classes机制也可以用来读取strings或将数据写入strings。String streams提供有缓冲区,但没有I/O通道;我们可以借着特殊函数来处理buffer/string。这项技术的一个主要用途就是以“独立于真实I/O装置以外”的方式来处理I/O。例如待输出文字的格式可以在string中设定,然后再将string发送到某个输出通道。

(1)针对string而定义的stream classes,与file stream 和 stream base classes 之间的关系一样。如图13.3描述了其间的继承体系。

表13.39 列出了String Streams的基本操作

一下程序说明如何使用string streams:

#include <iostream>
#include <sstream>
#include <bitset>
using namespace std; int main()
{
ostringstream os; // decimal and hexadecimal value
os << " dec :" << << hex << " hex :" << << endl;
cout << os.str() << endl;
// append floating value and bitset
bitset<> b();
os << " float :" << 4.67 << " bitset :" << b << endl;
// overwrite with octal value
os.seekp();
os << " oct :" << oct << ;
cout << os.str() << endl;
}
输出:
dec : hex : f oct : hex : f
float : 4.67 bitset :

处理string stream时,一个典型的错误是忘了使用str()提取字符串,而直接往stream输出。从编译器的角度看,这是合情合理的,因为此处的确存在一个转换操作可将参数转换为void*。于是stream的状态以地址形式被写入。

output string stream写入数据的一个典型应用就是,定义使用者自定义型别的output操作符。Input string stream主要用途是"按格式,从现有字符串中读取数据"。如:

int x;
float f;
std::string s = "3.7";
std::istringstream is(s);
is >> x >> f;

注意:str(s) 返回的字符串是s的副本。

(2)char* Stream Classes

char* stream classes 只是为了向下兼容才被保留下来。这里只做一个简单介绍:

char* stream classes 是特别为字符型别char而定义的,包括:

class istrstream,class ostrstream,class strstream,class istrstreambuf,都在<strstream>头文件中被定义。

istrstream的初始化有两种做法,一是“以字符串终止符号‘\0’为结尾”的字符序列;一是将字符数量也当做参数传给构造函数。 使用char* stream作为字符串时必须十分小心,因为它和string stream不同,它并不负责存储字符序列所需的内存。利用成员函数str(),字符序列可以和其调用者一起共同管理内存。除非stream 被初始化为定长缓冲区(这么一来stream就不必负责)。

被冻结的char* stream可以恢复其正常状态。要做到这一点,必须调用成员函数freeze()并传入参数false。这么一来字符序列的所有权就转给了stream对象。这也是释放字符序列内存的唯一安全做法(调用者通过调用freeze(false)将所有权转给stream对象;stream对象通过调用str()将内存所有权转移给了调用者)。如下:

// 注意
// 1. 调用成员函数freeze()并传入参数false将内存所有权转给stream对象,是释放字符序列内存的唯一安全做法
// 2. 如果调用str(),stream便不能再修改字符序列。Stream会暗中调用成员函数freeze(),冻结字符序列
// 3. 成员函数str()不会附加字符终止符号('\0')。我们只有直接在stream内加上该特殊字符,才能结束字符序列。
float x;
...
std::ostrstream buffer;
foo(buffer.str());
buffer.freeze(false); buffer.seekp(, ios::beg);
buffer << "once more float x :" << x << std::endl;
foo(buffer.str());
buffer.freeze(false);

(3)以辅助函数完成I/O
如果执行I/O操作符时需要存取对象的私有成员,标准操作符应该将实际任务委派给辅助的成员函数。这种技术允许具有多态性的读写函数,如下:

class Fraction
{
...
public:
virtual void printOn(std::ostream& strm) const; // output
virtual void scanFrom(std::istream& strm); // input
...
}; std::ostream& operator<< (std::ostream& strm, const Fraction& f)
{
f.printOn(strm);
return strm;
} std::istream& operator>> (std::istream& strm, Fraction& f)
{
f.scanFrom(strm);
return strm;
}

如果你的classes并不打算被当做基类,那么你的I/O操作符可以设计为其friends。但是要注意,一旦用上了继承,这种方法(使用friends函数)就会有很大的局限性。friend函数不能成为虚函数,所以你的程序可能会调用错误的函数。例如,如果某个base class reference 实际指向一个derived class object,并被当做input操作符的参数,则被调用的将是base class的操作符。为了避免出现这种情况,derived classs不得不实作自己的I/O操作符。因此,先前的实作手法比friend函数手法通用得多。故此:

1. classes打算作为基类——>那么使用虚函数;

2. 不做基类——>friends函数;

3. 基类,使用friends函数——>有很大的局限性。

13.13 Stream Buffer Classes

一如前面所介绍的,stream并不负责实际读写操作,而是委托给stream buffers(缓冲区)完成。

(1)对于stream缓冲区的使用者来说,class basic_streambuf只是发送(sent)或提取(extracted)字符的地方。

表13.41列出两个public函数,用来写入(到缓冲区)字符。

表13.42列出用来(从缓冲区)读取字符的public成员函数。

(2)Stream缓冲区迭代器(Buffer Iterators) 针对“无格式I/O”使用stream成员函数的另一种做法就是:采用stream缓冲区的迭代器类别(iterator classes)。这些类别所提供的迭代器1. 符合Input迭代器和Output迭代器的规格;2. 从stream缓冲区读取或写入单一字符。这么一来就能够将字符层级的I/O纳入C++标准程序库的算法管辖范围内了。

表13.44 列出Output Stream缓冲区迭代器的各项操作

例子如下:

// 以下程序片段使用ostreambuf_iterator 将字符串写入一个stream缓冲区
// create iterator for buffer of output stream cout
std::ostreambuf_iterator<char> bufWriter(std::cout);
std::string hello("hello, world\n");
std::copy(hello.begin(), hello.end(), bufWriter);

表13.45 列出 Input Stream 缓冲区迭代器的各项操作

注意:

1. 当两个stream缓冲区迭代器都是(或都不是)end-of-stream迭代器时,两者被视为相等(至于其output缓冲区是否相同,并不影响)。

2. 获得一个end-of-stream迭代器的可行方法是以default构造函数产生一个迭代器。此外,如果试图将迭代器移至stream尾部之外(也就是说如果sbumpc()返回traits_type::eof()),istreambuf_iterator就会成为一个end-of-stream迭代器。

//
#include <iostream>
#include <iterator>
using namespace std; int main()
{
// input stream buffer iterator for in
istreambuf_iterator<char> inpos(cin); // end-of-stream iterator, default 构造函数
istreambuf_iterator<char> endpos; // output stream buffer iterator for cout
ostreambuf_iterator<char> outpos(cout); // while input iterator is valid
while (inpos != endpos)
{
*outpos = *inpos; // assign its value to the output iterator
++inpos;
++outpos;
}
}
 

《C++标准程序库》笔记之四的更多相关文章

  1. Oracle学习笔记之四sp1,Oracle 11g的常用函数

    从Oracle学习笔记之四,SQL语言入门中摘出来的,独立成一章节 3.1 字符类函数 ASCII(c)和CHR(i)    分别用于返回一个字符的ASCII码和返回给定ASCII值所对应的字符. C ...

  2. C++标准程序库笔记之一

    本篇博客笔记顺序大体按照<C++标准程序库(第1版)>各章节顺序编排. ---------------------------------------------------------- ...

  3. 《C#图解教程》读书笔记之四:类和继承

    本篇已收录至<C#图解教程>读书笔记目录贴,点击访问该目录可获取更多内容. 一.万物之宗:Object (1)除了特殊的Object类,其他所有类都是派生类,即使他们没有显示基类定义. ( ...

  4. Django 学习笔记之四 QuerySet常用方法

    QuerySet是一个可遍历结构,它本质上是一个给定的模型的对象列表,是有序的. 1.建立模型: 2.数据文件(test.txt) 3.文件数据入库(默认的sqlite3) 入库之前执行 数据库同步命 ...

  5. linux网络编程学习笔记之四 -----多-threaded服务器

    对于使用过程中并发.通过实现更轻量级线程. 每个线程都是一个独立的逻辑流. 主题是CPU在执行调度的最小独立单位,这个过程是资源分配单元.当然,这是在微内核操作系统说.总之,这是唯一的一个操作系统内核 ...

  6. 【Visual C++】游戏编程学习笔记之四:透明动画实现

    本系列文章由@二货梦想家张程 所写,转载请注明出处. 本文章链接:http://blog.csdn.net/terence1212/article/details/44224963 作者:ZeeCod ...

  7. Oracle学习笔记之四,SQL语言入门

    1. SQL语言概述 1.1 SQL语言特点 集合性,SQL可以的高层的数据结构上进行工作,工作时不是单条地处理记录,而对数据进行成组的处理. 统一性,操作任务主要包括:查询数据:插入.修改和删除数据 ...

  8. java并发笔记之四synchronized 锁的膨胀过程(锁的升级过程)深入剖析

    警告⚠️:本文耗时很长,先做好心理准备,建议PC端浏览器浏览效果更佳. 本篇我们讲通过大量实例代码及hotspot源码分析偏向锁(批量重偏向.批量撤销).轻量级锁.重量级锁及锁的膨胀过程(也就是锁的升 ...

  9. Vue.js 学习笔记之四:Vue 组件基础

    到目前为止,这个系列的笔记所展示的都是一些极为简单的单页面 Web 应用程序,并且页面上通常只有几个简单的交互元素.但在实际生产环境中,Web 应用程序的用户界面往往是由多个复杂的页面共同组成的.这时 ...

随机推荐

  1. SDRAM 学习笔记(三)

    上图是terasic公司提供的SDRAM控制器,大部分已经封装好,我们需要修改其中部分代码,以此来实现我们自己需要的功能. 1.PLL时钟设定 首先上面的sdram_pll.v中产生上一篇博客所需要的 ...

  2. Python之生成二面体群元素

    from sympy.combinatorics.named_groups import DihedralGroup from collections import Counter n = 12 G ...

  3. Ogre2.1 结合OpenGL3+高效渲染

    在DX10与OpenGL3+之前,二者都是固定管线与可编程管线的混合,其中对应Ogre1.x的版本,也是结合固定与可编程管线设计.转眼到了OpenGL3+与DX10后,固定管线都被移除了,相对应着色器 ...

  4. OpenGL OBJ模型加载.

    在我们前面绘制一个屋,我们可以看到,需要每个立方体一个一个的自己来推并且还要处理位置信息.代码量大并且要时间.现在我们通过加载模型文件的方法来生成模型文件,比较流行的3D模型文件有OBJ,FBX,da ...

  5. 【Centos】【Python】【Flask】阿里云上部署一个 flask 项目

    1. 安装 python3 和 pip3 参考:http://www.cnblogs.com/mqxs/p/8692870.html 2.安装 lnmpa 集成开发环境 参考:http://www.c ...

  6. e612. Moving the Focus to the Next or Previous Focusable Component

    The methods to move the focus to the next or to the previous focusable component are Component.trans ...

  7. 【转】Asp.net MVC 通过自定义ControllerFactory实现构造器注入(重写DefaultControllerFactory)

    [转]Asp.net MVC 通过自定义ControllerFactory实现构造器注入 一.重写ControllerFactory的GetControllerInstance ControllerF ...

  8. Git 基础 - 远程仓库的使用

    远程仓库的使用 要参与任何一个 Git 项目的协作,必须要了解该如何管理远程仓库.远程仓库是指托管在网络上的项目仓库,可能会有好多个,其中有些你只能读,另外有些可以写.同他人协作开发某个项目时,需要管 ...

  9. 启动zookeeper时出现的问题

    zkEnv.cmd @echo off REM Licensed to the Apache Software Foundation (ASF) under one or more REM contr ...

  10. 用Fiddler可以设置浏览器的UA 和 手动 --Chrome模拟手机浏览器(iOS/Android)的三种方法,亲测无误!

    附加以一种软件的方法是:用Fiddler可以设置浏览器的UA 以下3种方法是手动的 通过伪装User-Agent,将浏览器模拟成Android设备. 第一种方法:新建Chrome快捷方式 右击桌面上的 ...