首先,我们还有很多的类没有分析:istream,filestream,filebuf,还有对应得迭代器。第二,即使前面的分析也不能说明I/O的使用规范。如果想要熟练的使用I/O,建议读《C++标准程序库》,那是一本经典。第三,流的格式化读取和输出大部分依靠的是locale,而我们前面很少涉及,现在还看不懂源码,只能以后分析了。
下面摘抄《c++标准程序库》中关于如何自定义I/O操作符的说明。
一,对于格式化化读取或者输入。
什么是格式化操作,就是有格式。例如一个文件里都是float,我们就可以按float读取。因为格式化操作都是转交给locale对象的,而根据流中预定义的操作,我们使用operator<<和operator>>来完成格式化处理。所以这里的主题是:如何重载这两个操作符?
(1)这里完全抄袭《c++标准程序库》中的例子:对于分数Fraction对象,按“分子/分母”格式,进行格式化输入和输出。
inline std::ostream& operator<<(ostream& strm,const Fraction& f)
{
strm<<f.numerator()<<'/'<<f.denominator();
return strm;
}
这是大多数人的重载方式,但是有两个缺点:
1.只适用于ostream即:字符型别为char的stream。
2.如果字符宽度被设定,则只会施加于分子上。
改进的重载模式:
template<class charT,class traits>
inline std::basic_ostream<charT,traits>&
operator<<(std::basic_ostream<charT,traits>& strm,const Fraction& f)
{
basic_ostringstream<charT,traits> s;
s.copyfmt(strm);
s.width(0);//令s的宽度为0,这样对于这个f,就没有填充宽度
s<<f.numerator()<<'/'<<f.denominator();
strm<<s.str();
return strm;
}
(2)同理对于重载:operator>>,我们需要的是检查是否正确:
template<class charT,class traits>
inline std::basic_ostream<charT,traits>&
operator>>(std::basic_ostream<charT,traits>& strm, Fraction& f)
{
int n,d;
strm>>n;
//检查后面是否是'/'
if(strm.peek()=='/'){
strm.ignore();
strm>>d;
}else{
d=1;
}
//检查分母是否为0
if(d==0){
strm.setstate(ios::failbit);
return strm;
}
//只有上述条件都符合,才能构建fraction'对象
if(strm){
f=Fraction(n.d);
}
return strm;
}
(3),对于具有虚拟继承和多态特性的输入输出,我们必须借助于辅助函数,然后在<<和>>中调用辅助函数;
二,以非格式化函数完成输入输出
非格式化就是我们只能一个一个字符的读取,不能直接读取int或者float:
对于这类函数,我们应该直接使用缓冲类的接口,因为这样就没有sentry的代价,否则,每调用一次<<或>>就会产生一个sentry。
流中有很多预定义的非格式化输入输出函数:如write,read。。。
三:直接使用stream缓冲区:
进行无格式化I/O时,我们尽量使用缓冲区操作,对于缓冲区的操作接口,这里不列出。
主要说明的是我们前面文章提到的_Myt& operator<<(_Mysb *_Strbuf)操作符:
cout<<cin.rdbuf();
cin>>noskipws>>cout.rdbuf();
由源码我们得知:会将缓冲区的内容全部输入和输出,同时缓冲区的指针也移动到末尾。
对于格式化的I/O,我们有时候出于效率的考虑,也会直接使用缓冲区,因为格式化的工作交给了locale,我们必须自己使用locale对象操作,这样的话就不必总是构造sentry对象;但是----不安全!
四,自定义I/O操作符的惯例:
(1)你的输出格式应该能让其他的使用者使用input操作符无损的读取数据,尤其是strings的空格问题;
(2)进行I/O时,应该考虑现有的stream格式规范,尤其是改写宽度时;
(3)如果发生错误,应设立相应的状态位;
(4)如果发生错误,原对象不应有任何改变。如果读取多个数据,读入的数据应该在被传递到对象前,先存储于辅助对象;
(5)输出不应以new line符号结束
(6)如果检测到某个格式错误,应该尽可能的不读取任何字符。