欢迎访问 生活随笔!

生活随笔

当前位置: 首页 > 编程语言 > c/c++ >内容正文

c/c++

C++IO流,istream status(状态位),cout,cin,if(非fs)/while(cin)【C++IO流】(58)

发布时间:2025/3/20 c/c++ 44 豆豆
生活随笔 收集整理的这篇文章主要介绍了 C++IO流,istream status(状态位),cout,cin,if(非fs)/while(cin)【C++IO流】(58) 小编觉得挺不错的,现在分享给大家,帮大家做个参考.

  • IO流
  • IO流类
    • Hierarchy(层次架构)
    • 流类特性(从ios继承下来的共有的特性)
      • 不可赋值和复制
      • 缓冲
      • 重载了<< >>
  • istream status(状态位)
    • 状态位的意义
    • ios_base.h 中的状态位
    • 状态位操作函数
    • 测试与结论
    • 结论
  • 应用
  • cout
    • 格式输出
    • 成员函数
  • cin
    • >>
    • get
      • 声明
      • get()/get(&)
      • get(s,n)/get(s,n,d)
      • 小结
    • getline
      • 声明
      • 测试
      • cin输入字符串中的空格
    • other
      • 声明
      • 测试
  • if(!fs)/while(cin)
    • 合法性
    • operator void*()与 与 operator!()
    • 常见策略
    • 自测

IO流

cin 和 cout 充当了 scanf 和 printf 的功能。
但他们并不是函数,而是类对象。
那么我们就有必要了解一下,他们是哪些类的对象。

IO流类

Hierarchy(层次架构)

cin是isream的对象,cout是ostream的对象。

流类特性(从ios继承下来的共有的特性)

不可赋值和复制

fstream也是上面类中继承的一种。
我们之前使用:

ostream & <<(ostream & out ,xxx)

ostream & out 这里是 & 因为cin和cout都是全局的,所以我们在博客中说是没有必要赋值一份,实际上想要复制也是不能实现的,因为是不能赋值和复制的。

凡是上面给出的继承ios体系结构,都遵循不能赋值不能复制的规则

代码演示:

#include <iostream> #include <fstream> using namespace std;void foo(fstream fs) {} int main() {fstream fs1, fs2;fs1 = fs2; //报错,不允许赋值foo(fs); //报错,不允许复制return 0; }

解释:
上面赋值运算符重载和拷贝构造成员被private私有化,所以不允许赋值和拷贝。

不同于标准库其他 class 的"值语意",iostream 是"对象语意",即 iostream 是non-copyable。这是正确的,因为如果 fstream 代表一个文件的话,拷贝一个 fstream 对象意味着什么呢?表示打开了两个文件吗?如果销毁一个 fstream 对象,它会关闭文件句柄,那么另一个 fstream copy 对象会因此受影响吗?

C++ 同时支持"数据抽象"和"面向对象编程",其实主要就是"值语意"与"对象语意"的区别,标准库里的 complex<> 、pair<>、vector<>、 string 等等都是值语意,拷贝之后就与原对象脱离关系,就跟拷贝一个 int 一样。而我们自己写的 Employee class、TcpConnection class 通常是对象语意,拷贝一个 Employee 对象是没有意义的,一个雇员不会变成两个雇员,他也不会领两份薪水。拷贝 TcpConnection 对象也没有意义,系统里边只有一个 TCP 连接,拷贝 TcpConnection 对象不会让我们拥有两个连接。因此如果在 C++ 里做面向对象编程,写的 class 通常应该禁用 copy constructor 和 assignment operator。

缓冲

下面几种情况会导致刷缓冲
1,程序正常结束,作为 main 函数结束的一部分,将清空所有缓冲区。
2,缓冲区满,则会刷缓冲。
3,endl, flush 也会刷缓冲。

对比C语言printf函数刷新缓冲进行理解。

重载了<< >>

#include <iostream> #include <fstream> using namespace std; int main() {fstream fs("abc.txt", ios::in | ios::out | ios::trunc); //trunc等价于C语言文件操作w+if (!fs)cout << "open error" << endl;fs << 1 << " " << 2 << " " << 3;fs.seekg(0, ios::beg);int x, y, z;fs >> x >> y >> z;cout << x << y << z;return 0; }

运行结果为:

所有的流类都重载了<<和>>

istream status(状态位)

状态位的意义

大部分情况下,我们可能并不关心这些标志状态位,比如我们以前用到的 cin/cout。

但是在循环读写中,这些标志位确实有大用途。比如,用于判断文件结束标志的位。

ios_base.h 中的状态位

/// Indicates a loss of integrity in an input or output sequence (such /// as an irrecoverable read error from a file). static const iostate t badbit = _S_badbit; /// Indicates that an input operation reached the end of an input sequence. static const iostate t eofbit = _S_eofbit; /// Indicates that an input operation failed to read the expected /// characters, or that an output operation failed to generate the /// desired characters. static const iostate t failbit = _S_failbit; /// Indicates all is well. static const iostate t goodbit = _S_goodbit;

状态位操作函数

测试与结论

测试(通过成员函数来访问)

#include <iostream> using namespace std; int main() {int val;cout << "Before a bad input operation:"<< "\n cin.eof() : " << cin.eof()<< "\n cin.fail(): " << cin.fail()<< "\n cin.bad() : " << cin.bad()<< "\n cin.good(): " << cin.good() << endl;cin >> val; // control + D/Z or 'a'cout << "After a bad input operation:"<< "\n cin.eof() : " << cin.eof()<< "\n cin.fail(): " << cin.fail()<< "\n cin.bad() : " << cin.bad()<< "\n cin.good(): " << cin.good() << endl;cin.clear(); //标志位清空cout << "\n cin.eof() : " << cin.eof()<< "\n cin.fail(): " << cin.fail()<< "\n cin.bad() : " << cin.bad()<< "\n cin.good(): " << cin.good() << endl;return 0; }

运行结果为:

我们进行其他输入测试:

输入浮点类型数据没有问题。因为读取到的也是4个字节没有问题。

输入字符出现错误。字符所占1个字节,读取的时候读取4个字节,发生越界。

结论

bad 一般指的是硬件错误,不常用。

应用

#include <iostream> using namespace std; int main() {char ch;while(cin.get(ch),!cin.eof()){cout<<ch<<endl;}return 0; }

运行结果为:

cout

格式输出

成员函数

#include <iostream> #include <fstream> using namespace std; int main() {cout.put('a');cout.write("abcd", 2) << endl; //里面数字不能越界cout << 'a' << "abcd" << endl;return 0; }

运行结果为:

上面函数基本不会使用到,我们更多的会直接使用cin

cin

>>

#include <iostream> using namespace std; int main() {char ch;while (cin >> ch) //通过返回值判断真假{cout << ch << endl;}return 0; }

运行结果为:

get

声明

get()/get(&)

#include <iostream> using namespace std; int main() {char ch;while ((ch = cin.get()) != EOF){cout << ch << endl;}while (cin.get(ch)){cout << ch << endl;}return 0; }

运行结果为:

get(s,n)/get(s,n,d)

#include <iostream> using namespace std; int main() {char buf[10];while (cin.get(buf, 2)) //终止符为’\n’{cout << buf << endl;}return 0; }

运行结果为:

#include <iostream> using namespace std; int main() {char buf[10];while (cin.get(buf, 20, 'x')) //以字符’x’为结束标记{cout << buf << endl;}return 0; }

运行结果为:

小结

get 读取字符,遇到 eof 则退出。get 最多读到 n-1 个字符,遇到标志位或 eof,则退出。 越不过标志位。

上面两个设置结束标志’\n’和’x’的函数遇到\n和x会直接退出,不会再次循环。所以我们引入下面函数。

getline

声明

测试

#include <iostream> using namespace std; int main() {char buf[10];while (cin.getline(buf, 10,'x')){cout << buf << endl;}cout << "\ncin.eof() : " << cin.eof()<< "\ncin.fail(): " << cin.fail()<< "\ncin.bad() : " << cin.bad()<< "\ncin.good(): " << cin.good() << endl;return 0; }

运行结果为:

上面代码运行过程中,如果输入标志位之前的数据个数小于设置读取的最小个数,则会读取输入的数据并且跳过标志位继续读取,如果输入标志位之前的数据个数大于设置读取的最大个数,则会直接退出程序。

#include <iostream> using namespace std; int main() {char buf[10];while (cin.getline(buf, 10)){cout << buf << endl;}cout << "\ncin.eof() : " << cin.eof()<< "\ncin.fail(): " << cin.fail()<< "\ncin.bad() : " << cin.bad()<< "\ncin.good(): " << cin.good() << endl;return 0; }

运行结果为:

在读取 n-1 个字符前,遇到标志位,则会读到标志位前的字符。然后越过标志位继续读取。

若在读到 n-1 个字符前没有遇到标志位,则会退出。
也就说如果输入的字符个数小于读取的字符个数那么就会直接读取读取并且跳过标志位,如果输入的字符多于读取的个数则会退出。

cin输入字符串中的空格

之前我们使用cin输入字符串就会出现这样的问题:

#include <iostream> using namespace std; int main() {char buf[10];cin >> buf;cout << buf << endl;return 0; }

运行结果为:

但是如果使用下面方式:

#include <iostream> using namespace std; int main() {char buf[10];cin.getline(buf,10);cout << buf << endl;return 0; }

运行结果为:

other

声明

测试

#include <iostream> using namespace std; int main() {char ch[20];cin.get(ch, 20, '/'); // i like c/ i like C++ also/cout << "the first part is :" << ch << endl;cin.get(ch, 20, '/'); // i like c/ i like C++ also/cout << "this second part is :" << ch << endl;return 0; }

运行结果为:

我们可以看到get遇到/会直接结束。

#include <iostream> using namespace std; int main() {char ch[20];cin.getline(ch, 20, '/'); // i like c/ i like C++ also/cout << "the first part is :" << ch << endl;cin.getline(ch, 20, '/'); // i like c/ i like C++ also/cout << "this second part is :" << ch << endl;return 0; }

运行结果为:

get 越不过去的截止字符,getline 越过丢弃的截止字符,如何作一些,补偿措施呢?

#include <iostream> using namespace std; int main() {char ch[20];cin.get(ch, 20, '/'); // i like c/ i like C++ also/cout << "the first part is :" << ch << endl;cin.ignore(10, 'i'); //第一次读取到/之后,忽略,直到遇到i位置没并且连i也忽略掉。cin.get(ch, 20, '/'); // i like c/ i like C++ also/cout << "this second part is:" << ch << endl;return 0; }

运行结果为:

#include <iostream> using namespace std; int main() {char ch[20];cin.get(ch, 20, '/'); // i like c/ i like C++ also/cout << "the first part is :" << ch << endl;cin.ignore(10, 'i'); //第一次读取到/之后,往后忽略10个字节,//直到遇到i位置没并且连i也忽略掉。cin.putback('i');//忽略i之后再使用cin.putback('i');把忽略的i推进去。char peek = cin.peek(); //窥探一下,不影响底层指针的走动cout << "peek is :" << peek << endl; //打印窥探结果cin.get(ch, 20, '/');cout << "this second part is:" << ch << endl;return 0; }

运行结果为:

if(!fs)/while(cin)

#include <iostream> #include<fstream> using namespace std; int main() {fstream fs("cc.txt",ios::in); //只有读取文件的权限,没有创建文件的权限if (!fs) //!重载 等价于fs.operator!(){cout << "open error" << endl;}return 0; }

运行结果为:

#include <iostream> #include<fstream> using namespace std; int main() {char ch;//等价于while(cin>>ch,!cin.fail()) 也等价于while(cin)while (cin >> ch) {cout << ch << endl;}return 0; }

运行结果为:

上面代码中,体现出cin可以转化为bool,int,void * 等类型。对象自身发生转化。

当是一个对象的时候就会根据已经自身发生一个转化,转化函数,重载都要根据语境调用发生适当的转化。

合法性

在判断文件打开成功与否或是连续从流中读取数据时,就要用到对流对象的操作,比如 if(!fs)和 while(cin>>val),我们都知道 cin 是一个流对象,而>>运算符返回左边的流对象,也就是说 cin>>val 返回 cin,于是 while(cin>>val)就变成了 while(cin),问题就变成了一个流对象在判断语句中的合法性。

不管是 while(cin)还是 if(!fs),都是合法的,为什么呢?
我们自己定义一个类,然后定义该类的对象,然后使用 if 语句来判断它是不合法的。

这说明,流对象具有某种转换函数,可以将一个流对象转换成判断语句可以识别的类型

operator void*()与 与 operator!()

打开 iostream.h 文件,找到 cin 的定义,发现是来自于 istream.h,其中的模板类basic_istream 继承自 basic_ios,打开 basic_ios 的定义,发现它有两个重载函数。 operator void *() const 和operator!() const。这两个函数使得流对象可作为判断语句的内容。

/** * @brief The quick-and-easy status check. * * This allows you to write constructs such as * <code>if (!a_stream) ...</code> and <code>while(a_stream) ...</code> *///数 转化函数 A A 类对象> -> d void * * 对象 operator void*() const {return this->) fail() ? 0 : const_cast<basic_ios*>(this); }// 运算符重载数 函数 用 对象调用 operator! bool operator! !() const () {return this-> fail(); }

常见策略

//while the stream is OK while(cin) = => while(!cin.fail()) //if the stream is NOT OK if(!cin) = => if(cin.fail())

自测

#include<iostream> using namespace std;class A { public:A() {}~A() {}operator void* ()const //转化函数 A类对象 void * 对象{cout << "operator void* () cast to void*; ";return (void*)this;}//运算符重载函数 对象调用 operator! bool operator!() const (){cout << "bool operator!() return bool; ";return true;} }; int main() {A a;if (a) cout << "first" << endl;if (!a) cout << "second" << endl;return 0; }

运行结果为:

总结

以上是生活随笔为你收集整理的C++IO流,istream status(状态位),cout,cin,if(非fs)/while(cin)【C++IO流】(58)的全部内容,希望文章能够帮你解决所遇到的问题。

如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。