文章目录
- 前言
- gcc源码分析
- 总结
前言
在之前的文章C++snprintf和stringstream中说到,在stringstream中不知道是维护了一个流指针还是计数器,导致我们在使用了str()后再使用"<<"向其尾部增加字符会导致意想不到的效果,现在我又翻了下gcc源码,找到了答案。
gcc源码分析
我们先来看看str()的逻辑是怎样的:
void str(const __string_type& __s){ _M_stringbuf.str(__s); }
所以,此时的重点就应该在它所调用的重载str()中:
void str(const __string_type& __s)
{// Cannot use _M_string = __s, since v3 strings are COW// (not always true now but assign() always works)._M_string.assign(__s.data(), __s.size());_M_stringbuf_init(_M_mode);
}
根据gcc对函数的描述,我们将目光着重放在_M_stringbuf_init函数中,从命名上可以知道它会对stringstream的模式进行设置:
void _M_stringbuf_init(ios_base::openmode __mode)
{_M_mode = __mode;__size_type __len = 0;if (_M_mode & (ios_base::ate | ios_base::app))__len = _M_string.size();_M_sync(const_cast<char_type*>(_M_string.data()), 0, __len);
}
所以,调用这个函数的时候,传入的__mode就成了关键。
在str()中,传入的参数是__M_mode,在函数中我们却没有对它的定义,可知它是一个成员:
由此找到了它的定义如上图,该成员定义在类basic_stringbuf中,这个类不是stringstream继承关系中的一环。
这个类是个独立的类,用于在内存中操作字符串的缓冲区。它提供了类似于文件流的接口,允许读写字符串。这就是stringstream操作字符流的根本。我们能够通过控制它,对程序的IO流进行操作,但是如果我们不改变IO流的模式,我们就没法实现我们想要的效果。
即:不管是使用istringstream、ostringstream、stringstream都存在同样的问题,没法通过这三个类实现我们想要的效果,只能通过更改缓冲区的模式来实现,这是它们的实现导致的。
我们想要的效果是什么呢?
这里给个例子吧:
stringstream ss;
ss,str("Hello ");
ss << “World”;
在执行了这三行代码后,ss.str()的结果应该是”Hello world“,但是实际上是”World “
总结
可以发现,我们没法解决str()和“<<”一同使用导致的冲突问题,这是因为stringstream本就不支持更改流状态这个操作,会这样是由于缓冲区的模式导致的。
str()实际上会重置流的状态,包括清空缓冲区以及重新设置流的位置等信息。
如果想要设置字符串内容,应该在操作完成后再调用 str() 函数,而不是在写入操作中间调用。