getline源代码简析

这个代码的注释参考了许多网友的心得,也加了一些我自己的体会,供后来者参考。

C++标准库basic_istream类的getline成员函数有两个版本

basic_istream<Elem, Tr>& getline(
      char_type *_Str, 
     streamsize _Count
);
basic_istream<Elem, Tr>& getline(
     char_type *_Str, 
     streamsize _Count, 
      char_type _Delim
);
第一个参数为用户提供的字符数组首地址,第二个为数组长度,第三个为字符串分隔符。

第一个版本的成员函数的一个实现为

typedef basic_istream<_Elem, _Traits> _Myt;
typedef basic_ios<_Elem, _Traits> _Myios;
_Myt& getline(_Elem *_Str, streamsize _typedef basic_istream<_Elem, _Traits> _Myt;Count)
{// get up to _Count characters into NTCS, discard newline
   return (getline(_Str, _Count, _Myios::widen('\n')));
}

可见它最终还是调用了第二个版本的getline,只是把分隔符设成了常见的回车而已,所以下面我们来重点分析下第二个成员函数的实现。
_Myt& getline(_Elem *_Str, streamsize _Count, _Elem _Delim) 
{// get up to _Count characters into NTCS, discard _Delim 
 	_DEBUG_POINTER(_Str);    //判断传入指针的合法性
 	ios_base::iostate _State = ios_base::goodbit;  
 	_Chcount = 0; //从输入流中读取的字符数
 	const sentry _Ok(*this, true);
 	/*注:上面这句很关键,它关系到下面的if是否执行,也就是是否读输入流。这句从语法上看,是
 	sentry是一个class, _Ok是sentry类的一个const对象,构造这个对象时需要传入两个参数
 	第一个是流对象自身的引用,第二个表示对空白字符(如空格、制表符)的处理方式,为true时意味着不忽略空白字符,即一个字符一个字符的从输入流中提取。
 	*/
 	
 	if (_Ok && 0 < _Count) 
   	/* **************************************************************************
   	* sentry类内部重载了一个类型转换运算符,它把sentry类的实例转换成了一个bool表达式。
   	* 这个表达式返回sentry类的私有成员_Ok的值。
   	bool sentry::operator bool() const
   	* {	// test if _Ipfx succeeded
	*		return (_Ok);
   	* 	}
   	* _Ok这个成员的值由sentry类的构造函数
   	* 在初始化时设置,设置的过程比较麻烦,这里不做赘述(其实我也没看十分明白)。
   	* 但可以肯定的是,当输入流的状态是正常时,这个成员的值也是true,
   	* 反之,则是false。 
   	* 
   	* _Count是调用者传入的第二个参数,这里用做循环计数器的初值,以后每读一个字符,
   	* _Count的值会减一。
   	******************************************************************************/
 	{
    // state okay, use facet to extract 
    int_type _Metadelim = _Traits::to_int_type(_Delim); 
    int_type _Meta = _Myios::rdbuf()->sgetc();//从输入流读一个字符 
    for (; ; _Meta = _Myios::rdbuf()->snextc()) //snextc()从输入流中读取下一个字符
		if (_Traits::eq_int_type(_Traits::eof(), _Meta)) 
              {// end of file, quit 
               	_State |= ios_base::eofbit; 
 				break; 
 			   }//注:遇到文件尾,getline结束 
 		else if (_Meta == _Metadelim) {
			 // got a delimiter, discard it and quit 
 			++_Chcount;    //读取字符数+1
 			_Myios::rdbuf()->sbumpc();
 			/*注:上面这句把结束符读掉了,如果不指定结束符,那就是把'\n'读掉了。 
 			但回车符本身并没有拷贝到缓冲区中,
 			这样下次的读操作将从回车符后面的第一个字符开始,
 			*/
 			break; 
 		}/* 注:遇到结束符,getline结束,注意这里的顺序,它是先判断是否遇到结束符,后判断是否读入了指定个数的。 */
 		else if (--_Count <= 0) 
 		{// buffer full, quit 
 			_State |= ios_base::failbit; 
 			break; 
 		}
 		//注:读到了指定个数,执行到这里已经隐含了在指定个数的最后一位仍然不是结束符,
 		//因此该部分将输入流状态置为了错误。
 		//这直接导致了接下来的getline(或者get)以及>>运算符等读操作都不能正确执行) 
 		else {
 			// got a character, add it to string 
 			++_Chcount;  //读取字符数加1
 			*_Str++ = _Traits::to_char_type(_Meta); 
 		}//注:这一分支将读取到的单个字符拷贝到缓冲区中
 	} 
 	*_Str = _Elem();  //
 	/* add terminating null character /*注:前面这句为字符串加入了终止符'\0'
 	因为_Elem()构造了一个ascii码为0的字符对象*/
 	_Myios::setstate(_Chcount == 0 ? _State | ios_base::failbit : _State);
 	/*注:如果没有读入任何字符,要保持执行这一次getline之前的输入流状态,
 	否则根据这一次getline执行的情况,设置输入流为相应状态。 */
 	return (*this);   //返回输入流对象本身
} 
 






   

你可能感兴趣的:(ios,basic,buffer,character,facet,newline)