知识梳理:输入输出和模板

输入输出相关的类

与输入输出相关的类

  • istream用于输入的流类,cin就是该类的对象
  • ostream用于输出的流类,cout就是该类的对象
  • ifstream是用于从文件中读取数据的类
  • ostream是用于向文件中写入数据的类
  • fstream是既能从文件中读取数据又能向文件写入数据的类

标准流对象

输入流对象: cin 与标准输入设备相连
输出流对象: cout 与标准输出设备相连
		   cerr 与标准错误输出设备相连
		   clog 与标准错误输出设备相连

缺省情况下
		cerr<<"hello"<<endl;
		clog<<"hello"<<endl;
	   和cout<<"hello"<<endl;一样
  • cin对应于标准输入流,用于从键盘读取数据,也可以被重定向为从文件中读取数据
  • cout对应于标准输出流,用于向屏幕输出数据,也可以被重定向为向文件写入数据
  • cerr对应于标准错误输出流,用于向屏幕输出出错信息
  • clog对应于标准错误输出流,用于向屏幕输出出错信息
  • cerr和clog的区别在于cerr不使用缓冲区,直接向显示器输出信息,而输出到clog中的信息会先被存放在缓冲区,缓冲区满或者刷新的时候才输出到屏幕上

输出重定向

#include <iostream>
...
int x,y;
cin>>x>>y;
freopen("test.txt","w",stdout);//将标准输出重定向到test.txt文件中
if(y==0) cerr<<"error"<<endl;//除数为0,则在屏幕上输出错误信息
else cout<<x/y;//正确情况下,输出结果到test.txt文件中

输入重定向

#include <iostream>
...
double f;int n;
freopen("test.txt","r",stdin);//cin被重定向为从test.txt文件中读取数据
cin>>f>>n;//从文件中获取数据并存放在变量f,n的过程
cout<<f<<","<<n<<endl;//输出

判断输入流在哪里结束的方法

int x;
while(cin>>x)
{
	......
}
如果是从文件输入,如freopen("test.txt","r",stdin);那么读到文件末尾,输入流就算结束。如果是从键盘输入,则在单独一行输入Ctrl+Z代表输入流结束

istream类的成员函数

istream &getline(char *buf,int bufsize);
从输入流中获取bufsize-1个字符到缓冲区或读到"\n"为止,哪个先到算哪个

istream &getline(char *buf,int bufsize,char delim);
从输入流中读取bufsize-1个字符到缓冲区buf或者读到或碰到delim字符为止,哪个先到算哪个

上面两个函数都会自动在buf中读入数据的结尾加上"\0""\n"或delim都不会被读入buf,但会被从输入流中取走。如果输入流中"\n"或delim之前的字符个数达到或超过了bufsize个,就会导致读入出错。结果就是,虽然本次读入已经完成,但是之后的读入可能都会失败。可以用if(!cin.getline(......))判断输入是否结束。

bool eof();判断输入流是否结束
int peak();返回下一个字符,但不会从流中删除此字符
istream &putback(char c); 将字符c放回输入流中
istream &ignore(int nCount=1,int delim=EOF);从流中删除最多nCount个字符,遇到EOF结束

流操纵算子

需要头文件#include

整数流的基数:流操纵算子dec\otc\hex\setbase

int n=10;
cout<<n<<endl;//十进制输出
cout<<hex<<n<<endl;//十六进制输出
cout<<dec<<n<<endl;//十进制输出
cout<<otc<<n<<endl;//八进制输出

浮点数的精度(precision\setprecision)

precision是成员函数,调用方式是: cout.precision(5);
setprecision是流操作算子,调用方式为: cout<<setprecision(5);//可以连续输出

它们功能相同,都是采取四舍五入的方式取舍:
	指定输出浮点数的有效位数(非定点方式输出时)
	指定输出浮点数的小数点后的有效位数(定点方式输出时)
定点方式:小数点必须出现在个位数后面,科学计数法的要求
缺省的条件下都是非顶点方式
#include <iostream>
#include <iomanip>
......
double x=1234567.89,y=12.34567;
int n=1234567,m=12;
cout<<setprecision(6)<<x<<endl//浮点数最多输出6位有效数字
	<<y<<endl<<n<<endl<<m;
//输出的数值分别为1.23457e+006, 12.3457, 1234567, 12,整数不受其影响
cout<<setiosflags(ios::fixed)<<//以小数点位置固定的方式输出
	<<setprecision(6)<<x<<endl
	<<y<<endl<<n<<endl<<x;
//对应的输出结果为1234567.890000, 12.345670, 1234567, 12

cout<<setiosflags(ios::fixed)<<//以定点输出
	setprecision(6)<<x<<endl<<
	resetiosflags(ios::fixed)<<x;//取消以小数点位置固定的方式输出

设置域宽(setw\width)
setw/width两者功能相同,一个是成员函数,一个是流操作算子,调用方式不同

cin>>setw(4)或者cin.width(5);
cout<<setw(4)或者cout.width(4);

宽度设置有效性是一次性的,在每次读入和输出前都要设置宽度。

int w=4;
char string[10];
cin.width(5);//指定一次输入宽度为5,包含结尾的\0
while(cin>>string){//读取4位,再加上一位\0
	cout.width(w++);//指定输出宽度
	cout<<string<<endl;//若字符宽度不足,在前面补空格
	cin.width(5);//一次性的,每次都要重新输入
//输入1234567890
//输出:1234
		 5678
		   90
#include <iomanip>
#include <iostream>
int main()
{
	int n=141;
	double x=1234567.89,y=12.34567;
	//(1)分别以十六进制、十进制、八进制先后输出n
	cout<<hex<<n<<" "<<dex<<n<<" "<<otc<<n<<endl;
	//(2)保留5位有效数字
	cout<<setprecision(5)<<x<<" "<<y<<endl;
	//(3)保留小数点后面5位
	cout<<fixed<<setprecision(5)<<x<<" "<<y<<endl;
	//(4)科学计数法输出,且保留小数点后面5位
	cout<<scientfic<<setprecision(5)<<x<<" "<<y<<endl;
	//(5)非负数要显示+,输出宽度为12字符,宽度不足用'*'补齐
	cout<<showpos<<fixed<<setw(12)<<setfill('*')<<12.1<<endl;
	//(6)非负数不显示+,输出宽度为12字符,宽度不足则右边用填充字符表示
	cout<<noshowpoa<<setw(12)<<left<<12.1<<endl;
	//(7)输出宽度为12字符,宽度不足则左边用填充字符填充
	cout<<setw(12)<<right<<12.1<<endl;
	//(8)宽度不足时,负号和数值分列左右,中间用填充字符填充
	cout<<setw(12)<<internal<<-12.1<<endl;

用户自定义的流操纵算子
定义函数,返回值和参数都是ostream的引用

ostream &tab(ostream &output){
	return output<<'\t';
}
cout<<"aa"<<tab<<"bb"<<endl;
//输出aa	bb

因为iostream对<<进行了重载(成员函数)

ostream &operator
<<(ostream & (*p)(ostream &));
该函数内部会调用p所指向的函数,且以*this作为参数
hex\dec\oct都是函数

文件读写

可以将顺序文件看作一个有限字符构成的顺序字符流,然后像对cin,cout一样的读写。

#include <fstream>//包含头文件
ofstream outFile("clients.dat",ios::out|ios::binary);//创建文件
-| 表示同时满足这些条件
-clients.dat 要创建的文件的名字
-ios::out文件打开的方式,输出到文件,删除原有内容
	或ios::app输出到文件,保留原有内容
-ios::binary以二进制文件格式打开文件

也可以先创建ofstream对象,再用open函数打开

ofstream fout;
fout.open("test.out",ios::out|ios::binary);

判断打开是否成功

if(!fout){
	cout<<"File open error!"<<endl;
}
文件名可以给出绝对路径,也可以给出相对路径,就是在当前文件夹下找文件。

文件名的绝对路径和相对路径

绝对路径
"c:\\tmp\\mydir\\some.txt"

相对路径
当前盘符的跟目录下的tmp\dir\some.txt:  "\\tmp\\mydir\\some.txt"
当前文件夹的tmp子文件夹里面的:"tmp\\mydir\\some.txt" 
当前文件夹的父文件夹下面的tmp子文件夹里:"..\\tmp\\mydir\\some.txt"
当前文件夹的父文件夹的父文件夹下面的tmp子文件夹中:"..\\..\\tmp\\mydir\\some.txt" 

文件的读写指针

  • 对于输入文件,有一个读指针
  • 对于输出文件,有一个写指针
  • 对于输入输出文件,有一个读写指针
  • 标识文件操作的当前位置,该指针在哪里,读写操作就在哪里进行
ofstream fout("a1.out",ios::app);//以添加方式打开
long location=fout.tellp();//取得写指针的位置
location=10;
fout.seekp(location);//将写指针移动到弟10个字节处
fout.seekp(location,ios::beg);//从头数location
fout.seekp(location,ios::cur);//从当前位置数location
fout.seekp(location,ios::end);//从尾部数location
location可以为负值
ifstream fin("a1.in",ios::ate);//打开文件,定位文件指针到文件尾
long location=fin.tellg();//取得读指针位置
location=10L;
fin.seekg(location);//将读指针移动到弟10个字节处
fin.seekg(location,ios::beg);//从头数
fin.seekg(location,ios::cur);//从当前位置数
fin.seekg(location,ios::end);//从尾部数
location可以为负值

字符文件读写
因为文件也是流,所以流的成员函数和流操作算子也同样适用于文件流。

写一个程序,将文件in.txt里面的整数排序后,输出到out.txt
#include <iostream>
#include <fstream>
#include <vector>
#include <algorithm>
using namespace std;
int main()
{
	vector<int> v;//存放整数的容器
	ifstream srcFile("in.txt",ios::in);//读取文件
	ofstream destFile("out.txt",ios::out);//用来存放结果
	int x;
	while(srcFile>>x) v.push_back(x);//一个一个读取到容器里
	sort(v.begin,v.end);//排序
	for(int i=0;i<v.size();i++)//一个一个存放到结果文件中
		destFile<<v[i]<<" ";
	destFile.close();//关闭文件
	srcFile.close();
	return 0;
}

二进制文件读写

二进制读文件:ifstream和fstream的成员函数:istream& read(char *s,long n);

将文件读指针指向的地方的n个字节内容,读到内存地址s,然后将文件读指针向后移动n字节(以ios::in方式打开文件时,文件读指针开始指向文件开头)

二进制写文件:ofstream和fstream的成员函数:istream & write(const char *s,long n);

将内存地址s处的n个字节内容写入到文件中写指针指向的位置,然后将文件写指针向后移动n个字节(以ios::out方式打开文件时,文件写指针开始指向文件开头,以ios::app方式打开文件时,指向文件结尾)

ofstream fout("some.dat",ios::out|ios::binary);//创建文件
int x=10;
fout.write((const char *)()&x),sizeof(int));//将x以二进制形式写入文件
fout.close();
ifstream fin("some.dat",ios::in|ios::binary);//文件读指针
int y;
fin.read((char *)&y,sizeof(int));//读取文件并将内容存放到Y中
fin.close();

函数模板

提高程序的可重用性:继承和泛型程序设计

用函数模板解决:
template <class 类型参数1, class 类型参数2,......>
返回值类型 模板名(形参表)
{
	函数体
};

template <class T>
void swap(T &x, T &y)
{//例:交换两个数字数值的函数模板
	T tmp=x;
	x=y;
	y=tmp;
}
int n=1,m=2;
double f=1.2,g=2.3;
swap(n,m);//编译器自动生成void swap(int &,int &);函数
swap(f,g);//编译器自动生成void swap(double &,double &)函数

函数模板中可以有不止一个类型参数。
不通过参数实例化函数模板:

template <class T>
T Inc(T n)
{
	return (1+n);
}
cout<<Inc<double>(4)/2;//输出2.5,不通过参数实例化模板的例子

函数模板可以重载,只要它们的形参表或类型参数不同即可。

template<class T1,class T2>
void print(T1 arg1,T2 arg2){//两个类型
	cout<<arg1<<" "<<arg2;
}

template<class T>
void print(T arg1,T arg2){//一个类型
	cout<<arg1<<" "<<arg2;
}

函数模板和函数的次序
在有多个函数和函数模板名字相同的情况下,编译器如下处理一条函数调用语句:

  • 先找参数完全匹配的普通函数()非由函数模板实例化而得到的函数
  • 再找参数完全匹配的模板函数
  • 再找实参数经过自动类型转换后能够匹配的普通函数
  • 上面的都找不到,则报错
    匹配模板函数时,不进行类型自动转换

类模板

为了多快好省的定义出一批相似的类,可以定义类模板,然后由类模板生成不同的类。

类模板的定义

template <class 类型参数1class 类型参数2......>
class 类模板名
{
	成员函数和成员变量
};

类模板里成员函数的写法:
template <class 类型参数1class 类型参数2......>
返回值类型 类模板名<类型参数名列表>::成员函数名(参数表){......}

用类模板定义对象的写法
类模板名<真实类型参数表> 对象名(构造函数实参表)

编译器由类模板生成类的过程叫类模板的实例化。由类模板实例化得到的类叫模板类。

同一个类模板的两个模板类是不兼容的。函数模板可以作为类模板的成员。

类模板与非类型参数
类模板的<类型参数表>中可以出现非类型参数

类模板和派生

类模板从类模板派生
类模板从模板类派生
类模板从普通类派生
类B从普通类A派生,所以从B实例化得到的类,都以A为基类。
普通类从模板类派生

类模板和友元

  • 函数、类、类的成员函数作为类模板的友元
  • 函数模板作为类模板的友元
  • 函数模板作为类的友元
  • 类模板作为类模板的友元

类模板与静态成员变量

类模板中可以定义静态成员,那么从该类模板实例化得到的所有类都包含同样的静态成员

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值