时间:2014.05.27
地点:基地
---------------------------------------------------------------------------------------
一、关于std::cin和std::cout
cin和cout的类型是std::istream和std::ostream 。这两个类型又分别对应着
std::basic_istream<char> std::basic_ostream<char>
即,是分别是这二者typedef后的别名而已,当然,这里省略的模板的默认参数,完整来说是:
std::basic_istream<char,std::char_traits<char>> std::basic_ostream<char,std::char_traits<char>>
---------------------------------------------------------------------------------------
二、流的rdbuf()成员函数
流的rdbuf()成员函数返回某个流对象说使用的streambbuf指针,该指针直接指向流底层使用的streambuf,正如前面一篇博文所说,绕过上层直接操作流的底层非常高效。另外,我的操作符operator<<()也接受这样一个指向streambuf的指针,于是对于某些操作,就像把一个池子的水端起来直接倒往另外一个水池一样,比起架起水管来让细水漫流是不是很爽?
---------------------------------------------------------------------------------------
三、应用
假设我们要写一个ECHO程序,简单的相应输入,能从cin获得输入,然后将输出发送到cout,也能从一个名为infile的文件获得输入,输往outfile文件。
有一条设计准则:
尽量提高可读性,避免撰写精简代码,避免晦涩。因为简洁代码难以理解和维护。
还有一条设计准则
尽量提高可扩充性
通过分析,我们知道,从获取输入到输出这个过程中,我们还可能要对数据进行一些处理,比如将字符进行大小写转换,删除一些字符,字符统计等,因此,我们需要将这一任务独立出来。
于是又有一条设计准则:
尽量提高封装性,将关系分离。
先来看整个程序框架:
int main(int argc, char* argv[]) { using namespace std; fstream in, out; if (argc > 1) in.open(argv[1], fstream::in, fstream::binary); if (argc > 2) out.open(argv[2], fstream::out | fstream::binary); Process(in.is_open()?in:cin,out.is_open()?out:cout); return EXIT_SUCCESS; }
现在的问题是怎么去实现Process函数,我们将处理数据的任务交付给它,它应该能够处理不同的参数类型,我们想到的是多态,C++中有四种方式可获得多态行为:虚函数,模板,重载,转换。
3.1模板(编译时多态)
通过模板实现多态也叫编译时多态,提供有被传递对象的合适接口,实现如下:
template<typename In, typename Out> void Process(In& in, Out& out) { ....一些数据处理, //或者只是简单的这样:out << in.rdbuf(); }
3.2虚函数(运行时多态)
通过虚函数的方式的多态也叫运行时多态,它声明的参数类型是一个具有可能的接口们对应的公共基类即可。比如一种可能的实现如下:
void Process(basic_istream<char>& in,basic_ostream<char>& out) { //...执行某些操作 //或者只是这样 out<<in.rdbuf(); //把一池子水直接倒入另外一个池子 }
这种方式具有一定的依赖性,它要求输入和输出必须分别从basic_istream<char>和basic_ostream<char>派生。(我们的istream,ostream,iostream,ifstream,ofstream,stringstream等都分别从二者派生而来,可如果不是呢,比如宽字符流是基于wchar_t的。或者有些流有着用户自定义的traits。于是为了提高可扩充性,应该使用模板,让编译器去推导这些合适的参数
template<typename C,typename T> void Process(basic_istream<C,T>& in,baise_ostream<C,T>& out) { ...... }
但显然第一种方式比较看起来舒服。只要可能,一段代码一个函数或一个类,应该集中只知道和负责处理一件事。比如这里,程序包括两部分,一部分代码知道输入/输出源和目标中的可能区别,到底使用什么样的流,另一部分知道如何真正的执行处理。通过将这两部分分离,使得代码用途更加清晰,易于理解和阅读。
---------------------------------------------------------------------------------------
四、总结
避免写出来的代码只能解决当前问题,几乎任何时候若能写出可扩充的方案,都是更好的选择。但在编写专用代码只解决当前问题(短视,难以扩充)和编写一个宏大的通用框架去解决本来很简单的问题(追求过度设计)之间要自己琢磨取得最佳平衡。第一种方法简单而灵活,更能适应新要求,没有束缚,不仅仅只能和iostream体系打交道。在函数或类的设计中,我们应该尽量考虑可扩充性。稍微多去思考下,当前正在解决的问题是不是某个更通用问题的特例,于是应该还多做一点工作,而不要仅仅满足于当前。
流和几条设计准则