C++——IOStream

本文介绍了C/C++中的IO概念,探讨了C++标准IO流如cin/cout/cerr/clog的特点,以及它们与C语言printf/scanf的不同。还详细讲解了流的连续性和终止规则,以及文件IO流的区别,包括二进制和文本文件的处理方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


什么是IO?

C语言和C++,我们其实已经接触到了两个IO的概念

#include<stdio.h>
#include<iostream>

iostream,便是IO流,其中I表示in,O表示out,代表着用户的输入和终端的输出。在之前的C++语法中,我们都很少去考虑输入输出,而是直接用固定的测试用例来验证结果。但是对于大多数的程序都是传递给用户进行互动,IO流在实际中才是最应该考虑到的问题。


什么是流?

和我们常了解的水流电流等一样,流是一段连续且有方向的概念。其中IO是流的两个方向,In是用户不断向内存内输入数据,Out是终端向外输出数据。但是流是连续的,数据捕获却并非连续,用户可以不断向内存中输入,但是程序只会捕获其需要的片段,剩下没有被捕获到的数据留在缓冲区待命,一直到程序再次捕获缓冲区的数据。


C++中的IO流

C语言里,最常用的IO流是熟知的printf和scanf,其中scanf和printf便分别表示了I和O。C++中也可以使用这两个来实现IO,但是为什么还要再另外写出cout和cin来代替C语言的IO?

C语言的IO最难用的地方,便是需要用特定的占位符来表示数据。但是有了泛型,我们很多时候并不知道他到底是个什么类型,于是占位符常常会出现一些奇奇怪怪的bug。

于是,C++为了解决这个问题,便利用了面向对象来重新实现了IO流,其中最大的改变便是可以自动识别出IO的类型,不需要用占位符来规定数据的类型。虽然这样有好有坏,但是printf又不是被禁用了,不好用的时候我们不用不就好了。

标准IO流

 C++标准库中提供了4个全局的IO流对象

  • cin,标准输入,将数据从键盘输入到内存
  • cout,标准输出,数据从内存输出到控制台
  • cerr,标准错误的输出
  • clog,日志输出

其中,cin的输入并非直接提取当时键盘的输入,而是键盘会输入到缓冲区,而cin从缓冲区中提取数据,剩下未被提取到的数据会留在缓冲区等待被下一次的提取。

并且,如果我们输入的对象是字符或者字符串,则空格和回车无法通过cin被输入,因为空格和回车会被作为分隔符,表示一串数据的输入完毕。

此时,便会有我们在做IO时经常会面临的一串代码:

while(cin>>a)
{
    //...
}

做IO时我们也许并不会考虑到这个问题:循环是如何判断中止的?

我们都知道,对于一个流插入,其返回值是一个istream对象;同样,对于一个流提取,其返回值是一个ostream对象。对象转换为需要被判断的bool值,编译器是没有办法完成这一操作的。于是,STL中为其又加入了一个新语法——类型转换的重载。

operator bool( )

operator bool,其作用在需要被转换的时候,编译器会自动去调用这个重载函数,将其转换为合适的类型。这一重载函数没有返回值类型,或者说其返回值强制是被重载的类型,所以在其前不需要加上返回值的类型。

class A
{
public:
	A(int a)
		:_a(a)
	{}

	//打破了往常的0为false,其他为true,重新定义了true和false的判断方法
	operator bool()
	{
		if (_a > 10)
			return false;
		else
			return true;
	}
private:
	int _a;
};

int main()
{
	A a1(20);
	A a2(1);

	//调用了operator bool函数来进行类型的转换
	cout << (bool)a1 << endl;//false
	cout << (bool)a2 << endl;//true
}

其最大的意义便是修改了固定空为false其余为true的判断方式,从而让bool判断更加便利 

并且,流插入和流提取并不是当空即为终止,其必须要通过特定的特征按键才会终止(比如Windows下的ctrl+Z),通过这种方式修改了true和false的判断方式,从而可以人为去规定流插入和流提取的终止。 

同样,不仅只有bool可以进行运算符重载,其他类型也可以进行运算符重载,在需要的时候大大提高程序的灵活性。

文件IO流

文件IO流同样也存在着三个对象:

  •  ifstream,只输入用
  •  ofstream,只输出用
  • fstream,输入和输出

但是文件的读写有着两种方式:二进制文件和文本文件。用最简单的方式来区别两种文件:
二进制文件类似于浅拷贝,直接将所有数据原封不动拷贝到文件中;
而文本文件则是深拷贝,只将有效数据拷贝到文件里。 

举个例子,对于一个单链表,我们将其存入文件中

如果采用二进制文件,则是直接暴力复制其里面所有的数据,包括其头结点所存的值、头结点的next指针等值的二进制机器码,读取的时候也是读取二进制数据码。
但是,程序每次运行时地址是不同的,也就是复制了指针,再去访问那个指针则是一个野指针,读取到的数据是无效的。

如果采用文本文件,则需手动编写读写的方式,手动遍历每个结点,然后以字符串的形式将每个结点所存的值写入文本中,读取的时候也需要手动编写读取的方式,将文本中的字符串依次转换为合适的类型,重新装入链表的每一个结点里。

而文本操作的函数,因为在不同的库中可能会产生差异,并且大多数可能会自己去实现,所以需要用的时候查库便可以了。


以下是一个简单的C++文字RPG游戏的示例代码,其中包括了基本的角色、战斗和物品系统: ```c++ #include <iostream> #include <string> #include <cstdlib> #include <ctime> using namespace std; // 角色类 class Character { public: string name; int hp; int atk; int def; int gold; Character(string n, int h, int a, int d, int g) { name = n; hp = h; atk = a; def = d; gold = g; } // 攻击函数 void attack(Character& other) { int damage = atk - other.def; if (damage < 0) { damage = 0; } other.hp -= damage; cout << name << "攻击了" << other.name << ",造成了" << damage << "点伤害。" << endl; } // 是否死亡 bool isDead() { return hp <= 0; } }; // 物品类 class Item { public: string name; int price; int hp; int atk; int def; Item(string n, int p, int h, int a, int d) { name = n; price = p; hp = h; atk = a; def = d; } }; // 商店类 class Shop { public: Item items[3]; Shop() { items[0] = Item("草药", 10, 20, 0, 0); items[1] = Item("铁剑", 50, 0, 10, 0); items[2] = Item("铁甲", 100, 0, 0, 10); } // 显示商店物品 void showItems() { cout << "欢迎光临!以下是本店的物品:" << endl; for (int i = 0; i < 3; i++) { cout << i + 1 << ". " << items[i].name << " - " << items[i].price << "金币" << endl; } } // 购买物品 bool buy(Character& c, int choice) { if (c.gold < items[choice - 1].price) { cout << "金币不足,法购买!" << endl; return false; } c.gold -= items[choice - 1].price; c.hp += items[choice - 1].hp; c.atk += items[choice - 1].atk; c.def += items[choice - 1].def; cout << "购买成功!" << endl; return true; } }; // 战斗函数 void battle(Character& player, Character& enemy) { cout << "你遇到了一只" << enemy.name << ",准备战斗!" << endl; while (!player.isDead() && !enemy.isDead()) { player.attack(enemy); if (enemy.isDead()) { cout << enemy.name << "被你打败了!" << endl; player.gold += enemy.gold; return; } enemy.attack(player); if (player.isDead()) { cout << "你被" << enemy.name << "打败了!" << endl; return; } } } int main() { srand(time(NULL)); // 初始化随机数种子 // 初始化角色和商店 Character player("勇者", 100, 10, 5, 50); Character enemies[3] = { Character("史莱姆", 30, 5, 2, 10), Character("骷髅兵", 50, 10, 5, 20), Character("巨龙", 100, 20, 10, 50) }; Shop shop; // 游戏循环 while (true) { cout << "你的状态 - HP:" << player.hp << " ATK:" << player.atk << " DEF:" << player.def << " 金币:" << player.gold << endl; cout << "请选择操作:" << endl; cout << "1. 进入商店" << endl; cout << "2. 进行战斗" << endl; cout << "3. 离开游戏" << endl; int choice; cin >> choice; switch (choice) { case 1: shop.showItems(); cout << "请选择要购买的物品(输入编号):" << endl; cin >> choice; shop.buy(player, choice); break; case 2: battle(player, enemies[rand() % 3]); break; case 3: cout << "游戏结束,欢迎再次光临!" << endl; return 0; default: cout << "无效的操作!" << endl; break; } } return 0; } ```
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值