STL中getline的设计疏忽
方法getline,顾名思义,是获取流中的一行出来,在STL中全局方法getline的定义是:
getline(basic_istream<,>& _Istr, basic_stream<,>& _Str);
getline(_Elem* _Str, streamsize _Count);
这说法不对的
不只是不想引入依赖性, 由于 std::string 重载了从 istream, ostream 输入输出的, 是 std::string 已经依赖于流了, 如果流在去依赖 std::string 就造成循环依赖了.
看来不是不想, 是不能.
流输入输出的重载是在string类外部的,没有对string有依赖
实际上c++11中fstream的open已经支持string作为参数了
[解决办法]
循环依赖是允许的?
你能把这样的东西编译过吗
class A
{
public:
B b;
};
class B
{
public:
A a;
};
string的实现本身并不需要依赖istream,两者是相互独立的
实际的实现中,string头文件里只是一些string类的外部运算符重载和getline等公用函数,具体的string类通常在内部头文件里定义,istream可以去包含那个
不想增加依赖性这点我是赞同的,但只是不想而已,硬要做到还是可以的
istream的成员函数getline的第一个参数是char数组,不管是静态还是动态肯定是有限的,不能自动增长,因此getline时就不能超过这个数组的长度,必须加count限定
全局getline的第二个参数数string,可以自动增长,因此不需要限制长度
这也就是我比较困惑的地方,为什么STL的设计开发人员没想到将_Elem换成string呢?这样不就可以自增长了吗
瀑布汗!
因为流是在string类出现之前就先出现的,必须支持char*。
istream的成员函数getline的第一个参数是char数组,不管是静态还是动态肯定是有限的,不能自动增长,因此getline时就不能超过这个数组的长度,必须加count限定
全局getline的第二个参数数string,可以自动增长,因此不需要限制长度
这也就是我比较困惑的地方,为什么STL的设计开发人员没想到将_Elem换成string呢?这样不就可以自增长了吗
iostream出现在c++中的时候,stl还不存在,也没有string类。虽然现在已经发展到了basic_istream模板,但还是要考虑对老代码的兼容,这个getline就是那时候的遗留产物。
或许确如7楼与17楼所言,出现减少依赖的目的考虑,没有实现basic_istream::getline(basic_string<C,T,A> & )的版本。
不过,fstream不在乎增加依赖,而istream就在乎,似乎也不是很有道理吧……
我还有个猜想:
1:istream中要有char *版本的getline,因为要兼容老代码;
2:istream中是否有string版本的getline,似乎并不重要,getline(std::cin, line)和std::cin.getline(line)相比也没什么不方便的;
3:getline的实现对istream的实现细节依赖不大;
4:如果知道string的具体实现细节,getline或许可以实现得更高效——也许某个库会让string版本的getline成为string的友元。
这样的话,getline与string的关系比它与istream的关系更为紧密,不让它成为istream的成员似乎更合理一些。
虽然是在类外部, 但是标准库只有头文件, 没有 cpp, 还是会在头文件之间引入循环包含的.
fstream 可以使用 std::string 了, 那可能是因为 std::string 依赖的是 istream, 而不是 ifstream 所以依赖关系成了: istream <-- string <-- ifstream 还是能保持单向. 但是要在 istream 的 getline 函数里引入 string 就恼火了, 会造成循环依赖的.
//下面是glad.h
#ifndef __INCLUDE_FILE_GLAD_H___
#define __INCLUDE_FILE_GLAD_H___
#include<iosfwd>
class sad;
class glad
{
public:
void laugh( std::basic_ostream<char,std::char_traits<char> > & out, sad & other );
void stop( std::basic_ostream<char,std::char_traits<char> > & out);
};
#include "glad_impl.h"
#endif
//上面是glad.h
//下面是sad.h
#ifndef __INCLUDE_FILE_SAD_H___
#define __INCLUDE_FILE_SAD_H___
#include<iosfwd>
class glad;
class sad
{
public:
void cry( std::basic_ostream<char,std::char_traits<char> > & out, glad & other );
};
#include "sad_impl.h"
#endif
//上面是sad.h
//下面是glad_impl.h
#ifndef __INCLUDE_FILE_GLAD_IMPL_H___
#define __INCLUDE_FILE_GLAD_IMPL_H___
#include "glad.h"
#include "sad.h"
#include <iostream>
inline void glad::laugh( std::basic_ostream<char,std::char_traits<char> > & out, sad & other )
{
out << "我很高兴!我笑,我笑!我再笑!!\n";
other.cry( out, *this );
}
inline void glad::stop( std::basic_ostream<char,std::char_traits<char> > & out)
{
out <<"我不笑了。\n" ;
}
#endif
//上面是glad_impl.h
//下面是sad_impl.h
#ifndef __INCLUDE_FILE_SAD_IMPL_H___
#define __INCLUDE_FILE_SAD_IMPL_H___
#include "glad.h"
#include "sad.h"
#include <iostream>
inline void sad::cry( std::basic_ostream<char,std::char_traits<char> > & out, glad & other )
{
out << "我很悲伤,我哭了……\n";
other.stop(out);
}
#endif
//上面是sad_impl.h
同样的还有 fstream 的构造函数和 open 函数, 都是用 char* 做参数的, 而不是用 std::string, 因为不想引入过多的依赖性.
[解决办法]
全局的 getline 是定义在 string 头文件里面的.
basic_istream 类是不依赖于 std::string 的, 它都不知道有 std::string 可用, 就只能按传统的字符串方式了.