STL中getline的设计疏忽

hunterzone 2013-06-14 04:19:01
方法getline,顾名思义,是获取流中的一行出来,在STL中全局方法getline的定义是:

getline(basic_istream<,>& _Istr, basic_stream<,>& _Str);

在basic_istream中的定义是:

getline(_Elem* _Str, streamsize _Count);

既然是获去流中的一行出来,为什么basic_istream中的getline还得加个_Count限制,如果仅是考虑到内存空间的限制,那这种接口的设置不够合理,不知道大家怎么看?
...全文
424 35 打赏 收藏 转发到动态 举报
写回复
用AI写文章
35 条回复
切换为时间正序
请发表友善的回复…
发表回复
hunterzone 2013-06-15
  • 打赏
  • 举报
回复
引用 21 楼 taodm 的回复:
瀑布汗! 因为流是在string类出现之前就先出现的,必须支持char*。
可能就向大家所说的,非不能,实不为也,
橡木疙瘩 2013-06-15
  • 打赏
  • 举报
回复
或许确如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的成员似乎更合理一些。
ri_aje 2013-06-15
  • 打赏
  • 举报
回复
引用 21 楼 taodm 的回复:
瀑布汗! 因为流是在string类出现之前就先出现的,必须支持char*。
引用 22 楼 u010936098 的回复:
[quote=引用 5 楼 hunter_wwq 的回复:] [quote=引用 3 楼 rocktyt2 的回复:] istream的成员函数getline的第一个参数是char数组,不管是静态还是动态肯定是有限的,不能自动增长,因此getline时就不能超过这个数组的长度,必须加count限定 全局getline的第二个参数数string,可以自动增长,因此不需要限制长度
这也就是我比较困惑的地方,为什么STL的设计开发人员没想到将_Elem换成string呢?这样不就可以自增长了吗[/quote] iostream出现在c++中的时候,stl还不存在,也没有string类。虽然现在已经发展到了basic_istream模板,但还是要考虑对老代码的兼容,这个getline就是那时候的遗留产物。[/quote] 这种说法解释不了为什么 c++11 没有选择增加 istream::getline 重载接受 std::string&,因为这么做并不破坏现有的代码,并且能够带来楼主所说的方便性。尤其是 fstream::fstream 在新版标准中已经经修正能够接受 std::string 参数,说明标准委员会注意到此类的设计失误,在这种情况下,istream::getline 仍然没有支持 string,则必然有其他原因,比较赞同 #7 和 #17 的观点。
ri_aje 2013-06-15
  • 打赏
  • 举报
回复
引用 27 楼 u010936098 的回复:
[quote=引用 12 楼 adlay 的回复:] 虽然是在类外部, 但是标准库只有头文件, 没有 cpp, 还是会在头文件之间引入循环包含的. fstream 可以使用 std::string 了, 那可能是因为 std::string 依赖的是 istream, 而不是 ifstream 所以依赖关系成了: istream <-- string <-- ifstream 还是能保持单向. 但是要在 istream 的 getline 函数里引入 string 就恼火了, 会造成循环依赖的.
这并不是理由,标准库就算只有头文件(其实是STL只有头文件),不代表它只有<iostream>和<string>之类的头文件,其文件组织比我们看到的要复杂,同样可以把声明和实现分开,写一个forward declaration也很容易,写个简单的例子:

//下面是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

[/quote] 参看 #17 的观点。非不能也,实不为也。没必要为了杯水车薪的利益而大动干戈,std::getline 已经挺好了。
manpages 2013-06-15
  • 打赏
  • 举报
回复
getline(istream&, string&) 和 getline(char*, streamsize) 它们有性能上的不同。提供多个重载版本,就是为了给高级用户(高级程序员)提供更多选择。 string参数自动增长匹配所读取内容的长度,其内部实现势必要做更多工作,或许更多多余内存。 了解一下vector这种类库中类似的东西,在需要重新分配内存时,一般实现是怎么做的——分配更多、多的多的内存。 ---- char *fgets(char * restrict s, int n, FILE * restrict stream); istream& getline (istream& strm, string& str) istream& getline (istream&& strm, string& str) istream& getline (istream& strm, string& str, char delim) istream& getline (istream&& strm, string& str, char delim) istream& istream::getline (char* str, streamsize count) istream& istream::getline (char* str, streamsize count, char delim)
mujiok2003 2013-06-15
  • 打赏
  • 举报
回复
引用 6 楼 adlay 的回复:
全局的 getline 是定义在 string 头文件里面的. basic_istream 类是不依赖于 std::string 的, 它都不知道有 std::string 可用, 就只能按传统的字符串方式了.
同意。C++的流的扩展依靠运算符重载(<<, >>)
mujiok2003 2013-06-15
  • 打赏
  • 举报
回复
引用 7 楼 adlay 的回复:
同样的还有 fstream 的构造函数和 open 函数, 都是用 char* 做参数的, 而不是用 std::string, 因为不想引入过多的依赖性.
个人认为是为了遵守0开销原则,因为我们很多是时候在代码中把文件名写成字符串常量,没有必要创建std::string. 对于文件名已经由std::string保存的情况,确实有些不方便,不过也没有什么大不了的。
mujiok2003 2013-06-15
  • 打赏
  • 举报
回复
指定缓冲区而已,很常见阿。

_Elem* _Str, streamsize _Count
橡木疙瘩 2013-06-15
  • 打赏
  • 举报
回复
引用 12 楼 adlay 的回复:
虽然是在类外部, 但是标准库只有头文件, 没有 cpp, 还是会在头文件之间引入循环包含的. fstream 可以使用 std::string 了, 那可能是因为 std::string 依赖的是 istream, 而不是 ifstream 所以依赖关系成了: istream <-- string <-- ifstream 还是能保持单向. 但是要在 istream 的 getline 函数里引入 string 就恼火了, 会造成循环依赖的.
这并不是理由,标准库就算只有头文件(其实是STL只有头文件),不代表它只有<iostream>和<string>之类的头文件,其文件组织比我们看到的要复杂,同样可以把声明和实现分开,写一个forward declaration也很容易,写个简单的例子:

//下面是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

失散糖 2013-06-15
  • 打赏
  • 举报
回复
每天回帖即可获得10分可用分!
ri_aje 2013-06-15
  • 打赏
  • 举报
回复
靠,还把人名字拼错了,it's Scott Meyers.
ri_aje 2013-06-15
  • 打赏
  • 举报
回复
引用 24 楼 u010936098 的回复:
或许确如7楼与17楼所言,出现减少依赖的目的考虑,没有实现basic_istream::getline(basic_string<C,T,A> & )的版本。 不过,fstream不在乎增加依赖,而istream就在乎,似乎也不是很有道理吧……
看 #12 说的。
引用 24 楼 u010936098 的回复:
我还有个猜想: 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的成员似乎更合理一些。
对,Meyer 提倡能不用成员函数就不用,这样增加封装性。 很可惜没有标准委员会的人能帮咱们印证一下。
taodm 2013-06-15
  • 打赏
  • 举报
回复
算了,你觉得你懂了就行了。
引用 32 楼 hunter_wwq 的回复:
[quote=引用 21 楼 taodm 的回复:] 瀑布汗! 因为流是在string类出现之前就先出现的,必须支持char*。
可能就向大家所说的,非不能,实不为也,[/quote]
橡木疙瘩 2013-06-14
  • 打赏
  • 举报
回复
引用 5 楼 hunter_wwq 的回复:
[quote=引用 3 楼 rocktyt2 的回复:] istream的成员函数getline的第一个参数是char数组,不管是静态还是动态肯定是有限的,不能自动增长,因此getline时就不能超过这个数组的长度,必须加count限定 全局getline的第二个参数数string,可以自动增长,因此不需要限制长度
这也就是我比较困惑的地方,为什么STL的设计开发人员没想到将_Elem换成string呢?这样不就可以自增长了吗[/quote] iostream出现在c++中的时候,stl还不存在,也没有string类。虽然现在已经发展到了basic_istream模板,但还是要考虑对老代码的兼容,这个getline就是那时候的遗留产物。
taodm 2013-06-14
  • 打赏
  • 举报
回复
瀑布汗! 因为流是在string类出现之前就先出现的,必须支持char*。
www_adintr_com 2013-06-14
  • 打赏
  • 举报
回复
引用 17 楼 rocktyt2 的回复:
string的实现本身并不需要依赖istream,两者是相互独立的 实际的实现中,string头文件里只是一些string类的外部运算符重载和getline等公用函数,具体的string类通常在内部头文件里定义,istream可以去包含那个 不想增加依赖性这点我是赞同的,但只是不想而已,硬要做到还是可以的
要在接口里面使用内部类, 还是不太好吧. 确实是可以把 string 中依赖 istream 的提出去, 多分几个头文件来实现.
hunterzone 2013-06-14
  • 打赏
  • 举报
回复
@allay 你写的那个代码是直接依赖的,这样肯定编不过啊!
hunterzone 2013-06-14
  • 打赏
  • 举报
回复

#pragma once
#include <istream>
#include <string>

class istreamEx : public std::basic_istream<char, char_traits<char>>
{
public:
	_Myt& __CLR_OR_THIS_CALL getline(std::string& str)
	{
		return *this;
	}
};
这样做是没有问题的。
rocktyt 2013-06-14
  • 打赏
  • 举报
回复
string的实现本身并不需要依赖istream,两者是相互独立的 实际的实现中,string头文件里只是一些string类的外部运算符重载和getline等公用函数,具体的string类通常在内部头文件里定义,istream可以去包含那个 不想增加依赖性这点我是赞同的,但只是不想而已,硬要做到还是可以的
www_adintr_com 2013-06-14
  • 打赏
  • 举报
回复
派生的类当然可以, string 又没有依赖你的派生类.
加载更多回复(15)

64,680

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧