c++中ostream类的超详细说明
cpp加油站 52 0

根据前文,ostream类是c++标准输出流的一个基类,本篇详细介绍ostream类的主要成员函数用法。

1.ostream的构造函数

从ostream头文件中截取一部分关于构造函数的声明和定义,如下:

public:
//explicit用来防止由构造函数定义的隐式转换
explicit
      basic_ostream(__streambuf_type* __sb)
      { this->init(__sb); }

protected:
      basic_ostream()
      { this->init(0); }

#if __cplusplus >= 201103L
      // Non-standard constructor that does not call init()
      basic_ostream(basic_iostream<_CharT, _Traits>&) { }

      basic_ostream(const basic_ostream&) = delete;

      basic_ostream(basic_ostream&& __rhs)
      : __ios_type()
      { __ios_type::move(__rhs); }

      // 27.7.3.3 Assign/swap

      basic_ostream& operator=(const basic_ostream&) = delete;

      basic_ostream&
      operator=(basic_ostream&& __rhs)
      {
    swap(__rhs);
    return *this;
      }

可以看到ostream类的默认构造函数是保护类型,而带参数的构造函数则是公有的,根据public和protected的功能,我们要定义一个ostream对象,必须要在参数中传入streambuf类型的指针才可以,否则会报编译错误。

一个可用的例子如下:

#include <iostream>
#include <fstream>

using namespace std;

int main()
{
    filebuf buf;
    if ( buf.open("/proc/self/fd/1", ios::out) == nullptr )
    {
        cerr << "stdout open failed" << endl;
        return -1;
    }
    ostream out(&buf);
    return 0;
}

与istream一样,因为streambuf类型的构造函数是保护类型,不能直接使用,所以需要使用它的继承者stringbuf或者filebuf,这里使用了filebuf,并且我们输出错误信息没有使用cout,这里使用了ostream定义的另外一个实例cerr,会输出错误信息到标准错误输出。

ostream类与istream类一样,它的的拷贝构造函数和赋值函数也都是保护类型的,所以ostream是不允许拷贝或者赋值的,所以它也不能直接作为返回类型和参数传递,很多时候需要使用引用来进行传递。

2.左移位<<操作符

部分<<操作符函数原型如下:

    //重载一系列<<操作符,可以用于读取变量数据并放入到流缓冲区中
      __ostream_type&
      operator<<(long __n)
      { return _M_insert(__n); }

      __ostream_type&
      operator<<(unsigned long __n)
      { return _M_insert(__n); }

      __ostream_type&
      operator<<(bool __n)
      { return _M_insert(__n); }

      __ostream_type&
      operator<<(short __n);

      __ostream_type&
      operator<<(unsigned short __n)
      {
    // _GLIBCXX_RESOLVE_LIB_DEFECTS
    // 117. basic_ostream uses nonexistent num_put member functions.
    return _M_insert(static_cast<unsigned long>(__n));
      }

      __ostream_type&
      operator<<(int __n);

      __ostream_type&
      operator<<(unsigned int __n)
      {
    // _GLIBCXX_RESOLVE_LIB_DEFECTS
    // 117. basic_ostream uses nonexistent num_put member functions.
    return _M_insert(static_cast<unsigned long>(__n));
      }

<<操作符可用于将数据写入到输出流中,使用例子如下:

#include <iostream>
#include <fstream>

using namespace std;

int main()
{
    filebuf buf;
    if ( buf.open("/proc/self/fd/1", ios::out) == nullptr )
    {
        cerr << "stdout open failed" << endl;
        return -1;
    }
    ostream out(&buf);
    int i = 1234;
    long long ll = 1234567;
    out << i << endl << ll << endl;
    return 0;
}

这时我们猜测,命令行标准输出中应该会输出两行数字,编译后执行结果如下:

1234
1234567

从这里out变量用法来看,实际上就是实现了标准库中cout的功能,当然,我是猜测可能是这样实现的。

/proc/self/fd/1是linux系统中标准输出文件,所以打开这个文件操作的话,反映在程序执行的过程中,就是直接输出到标准输出。

3.put函数

ostream头文件中put函数原型如下:

      //往缓冲区中插入一个字符
      __ostream_type&
      put(char_type __c);

put函数使用例子如下:

#include <iostream>
#include <fstream>

using namespace std;

int main()
{
    filebuf buf;
    if ( buf.open("/proc/self/fd/1", ios::out) == nullptr )
    {
        cerr << "stdout open failed" << endl;
        return -1;
    }
    ostream out(&buf);
    char c = 'X';
    out.put('c').put('=').put(c).put('\n');
    return 0;
}

这里因为put函数返回的是ostream&类型,所以可以连着使用put函数,代码编译后执行结果如下:

[root@mylinux ~]# ./a.out 
c=X
[root@mylinux ~]#
4.write函数

ostream的write函数原型如下:

      //将__s指针所指向的字符串复制出来并插入到缓冲区中,最多插入__n个字符
      __ostream_type&
      write(const char_type* __s, streamsize __n);

用法如下:

#include <iostream>
#include <fstream>

using namespace std;

int main()
{
    filebuf buf;
    if ( buf.open("/proc/self/fd/1", ios::out) == nullptr )
    {
        cerr << "stdout open failed" << endl;
        return -1;
    }
    ostream out(&buf);
    if ( !out.good())
    {
        cerr << "stream buf state is bad" << endl;
        return -1;
    }
    out.write("aaa\n", 4);    
    return 0;
}

good函数是ostream继承于父类ios的一个成员函数,它用来检查流的状态是否正常,正常则返回true。

代码编译执行后结果如下:

[root@mylinux ~]# ./a.out 
aaa
[root@mylinux ~]#
5.flush函数

函数原型如下:

//将数据从缓冲区同步到存储介质中
__ostream_type&
      flush();

使用方法如下:

#include <iostream>
#include <fstream>

using namespace std;

int main()
{
    ofstream out("aaa.txt");
    if ( !out.good())
    {
        cerr << "stream buf state is bad" << endl;
        return -1;
    }
    for (int n=0; n<10; ++n)
      {
        out << n;
        //out.flush();
      }    
    while(1);
    out.close();
    return 0;
}

这里使用了ofstream类型,它是ostream的一个子类,所以对于flush用法是一样的,这里我们先把flush函数调用注释掉,此时去执行代码,然后查看aaa.txt文件,会发现数据并没有写入到文件中去,然后我们把注释取消,重新编译执行后,查看aaa.txt内容,会看到0123456789已经被写入到文件中去。

按照我的理解,ofstream在往文件中写入数据时,数据实际上是先写到缓冲区中,并没有写到文件中去,所以需要调用一个flush,来确保数据会从缓冲区写到输出设备,也就是文件中去。

这里有一个小插曲,我一开始使用了out << n << endl去往文件写数据,发现flush是不起作用的,一直很疑惑,后来看源代码才发现endl这个操纵算子,它会调用先往缓冲区写入一个换行符然后再调用flush函数,所以flush才会不起作用。

6.tellp函数

tellp函数原型如下:

//返回当前写缓冲区位置
     pos_type
      tellp();

使用例子如下:

#include <iostream>
#include <fstream>

using namespace std;

int main()
{
    ofstream out("aaa.txt");
    if ( !out.good())
    {
        cerr << "stream buf state is bad" << endl;
        return -1;
    }
    for (int n=0; n<10; ++n)
      {
        out << n << endl;
        out.flush();
      }    
    clog << "current pos is " << out.tellp() <<endl;
    out.close();
    return 0;
}

从代码分析,目前应该是写到了20的位置,编译执行,结果如下:

[root@mylinux~]# ./a.out 
current pos is 20
[root@mylinux~]# 
7.seekp函数

seekp函数原型如下:

      /**
     从当前位置开始,跳转pos个写位置
      */
      __ostream_type&
      seekp(pos_type pos);

      /**
      根据ios_base::seekdir定义的位置,跳转off个写位置
      */
       __ostream_type&
      seekp(off_type off, ios_base::seekdir);

实例如下:

#include <iostream>
#include <fstream>

using namespace std;

int main()
{
    ofstream out("aaa.txt");
    if ( !out.good())
    {
        cerr << "stream buf state is bad" << endl;
        return -1;
    }
    out << 1234567;
    clog << "first pos is " << out.tellp() <<endl;
    out.seekp(3);
    clog << "second pos is " << out.tellp() <<endl;
    out.seekp(1, ios::beg);
    clog << "third pos is " << out.tellp() <<endl;
    out.close();
    return 0;
}

输出结果如下:

[root@mylinux ~]# ./a.out 
first pos is 7
second pos is 3
third pos is 1
[root@mylinux ~]#

到这里,ostream类的public成员函数就介绍完毕啦,若有不对之处,欢迎指正。

评论区

索引目录