Chinaunix首页 | 论坛 | 博客
  • 博客访问: 240391
  • 博文数量: 127
  • 博客积分: 34
  • 博客等级: 民兵
  • 技术积分: 655
  • 用 户 组: 普通用户
  • 注册时间: 2012-10-03 10:53
文章分类

全部博文(127)

文章存档

2013年(19)

2012年(108)

分类:

2012-11-08 13:40:51

原文地址:C++ ostream与printf比较 作者:jialisoft

C++标准库,可以说为了保证其通用性和效率,真是绞尽脑汁、C++特性无所不用其极,而且还专门为了设计容器增加了模板的支持。C++的流当然也不是十全是美,这也跟C++的特性本身有关,还谈不上“糟糕”,更不能说“很糟糕”! 
    原因在于 
ostream<<”str “<<1<<”, str”<<2; 
这种方式使得ostream语句何时结束,对于日志、网络等一些自定义输出操作,就不知道何时应该实施真实的操作。另一个问题就是不能保证该语句的原子操作。其实这些问题可以通过一定的手段来解决。 
1) 结束符。可以通过一个特殊的字符来解决,如标准库中的std::ends,其实就是一个字符"/0",而对于二进制流,这样恐怕是不行的,可以通过下面的方法来处理。
 
#include 
#include 
#include 
using namespace std;
 
struct streamend { };
 
class LoggerStream : public stringstream //public std::ostrstream 
{
public:
static streamend end;
~LoggerStream( ) { DoPrint(); }
 
private:
void DoPrint( void )
{
cout // real string
this->str("");
}
private:
friend ostream& operator<<(ostream& os, const streamend& end);
};
 
streamend LoggerStream::end;
 
ostream& operator<<(ostream& os, const streamend& end)
{
#if 1         // 两种方法都可以,后者要求对RTTI的支持
ostream* pos = &os;
LoggerStream* pls = dynamic_cast(pos);
if( pls != NULL)
pls->DoPrint();
#else
if( typeid(os) == typeid(LoggerStream))
((LoggerStream*)(&os))->DoPrint();
#endif
return os;
}
#define LOG( content )  lstream<
int main() 
{
LoggerStream lstream; 
 
 
lstream << 1 << " hello world/n"< lstream <<"line 1"<
 
LOG( "str1"<<1<<"str"<<2);              
stringstream ss;
ss<<"hello"< return 0;
}
2)多线程支持。其实也可以通过扩展LOG宏来处理,假设为自定义类型增加互斥锁,分别使用Lock( ), Unlock( )方法进行上锁与解锁,这样可以将宏修改为
 
#define LOG( content )  do{ /
lstream.Lock(); /
lstream< lstream.Unlock( ); /
}while(0)
既然说到printf与流,不妨也比较一下两个的优缺点。 
1. 先说printf的优点,也就这一点了,那就是代码简洁,格式化方便,可以在格式化字符串里一次性将输出格式化。而ostream则需要一段一段地拆分,显得比较烦锁,特别是自定义输出类型的格式时,如格式化输出浮点的小数位数、十六进制输出等,用ostream更烦锁。 
2.ostream类型安全,而printf则不能保证类型安全。 
2.1)printf容易产生输出格式字符串错误。 
int i = –1; 
std::cout
使用printf的输出结果将是错误的,虽然“那谁”网友也说到GCC中对__attribute__的扩展,可以检测printf的格式化字符串,但对于%u仍然无可奈何,而且__attribute__移植性不好,其它的编译器不支持该特性。虽然一开始写的时候可以保证格式字符串的一致性,但谁能保证在一个大系统中,哪天unsigned变量不会被修改成signed,这样输出将不再正确。
 
2.2)printf类型错误时会造成程序崩溃。因为在64位主机上指针为8字节,而在32位系统中指针为4字节,如果使用格式符不当,会导致地址非法访问导致程序崩溃。而且,当printf提供的参数少于格式符时也会导致指针的非法访问,导致程序崩溃(GCC -Wall会给出警告) 
2.3) printf与string混用容易出错。printf是C的API,如果使用%s直接输出string变量,将有可能导致程序崩溃(VC做了非标准的处理,可以正确输出)。 
3.printf需要记很多格式字符,而使用ostream则不需要。 
4.当printf后跟的参数很多时,很容易将参数的顺序搞错,而使用ostream则不容易出现这种情况。 
5.使用sprintf等类似函数时需要自己处理缓冲区,处理不当容易产生缓冲区溢出,导致不可预知的错误。 
6. 效率。效率应该差不多,虽然cout调用函数次数比较多,但它不用解析格式字符串,效率应该并不多。在VC2010上测试即差别比较大,printf约是cout效率的10位以上,而在Cygwin下使用gcc 4.3.4测试则相差不多,printf效率约为cout的1.2倍左右,看来应该是VC库的效率问题。下面是测试代码:
 
int repeat = 2000;
long long ll = 1;
long l = 2;
short s=4;
char c = 'c';
float f = 5.5f;
double df = 6.6;
 
clock_t start = clock();
 
for(int i=0; i cout<<"ll="< ,ll, l, i, s, c, f, df);
}
end = clock();
clock_t elapse_printf = end - start;
 
cout<<"------------------- finished --------------------------"< <<"elapse_cout = "< <<"elapse_printf = "<
 
“那谁”网友一再不承认这是“解决”方案,认为是对问题的“规避”措施;一再强调说是为了说明C++流设计有缺陷,极其推崇GCC的__attribute__检查制机制,但事实证明它并不能解决问题,对于"%u”输出格式不能保证数据正确个输出,而且不能给出警告,所以很容易导致输出数据的错误。如果想彻底解决问题,通过C++标准,严格定义格式输出,并在编译期进行强类型检查,这但样做不太合适,至少不适合定义到编译器的标准中去,因为printf只是一个库函数一样,语言不可能因为某个函数制定标准。 boost::format和fastformat也都是通过格式字符串来解析,无论功能如何强大,都不可能在编译器层面解决类型检查的问题,输出错误也就很难避免。
 
流操作符只是一个普通的运算符,它不可能预测未来如何使用它,不可能有办法在语法上强制用户多调用它一次,不仅C++做不到,任何一种语言也都不可能做到。如果要彻底解决格式化的问题,但是可以通过RTTI来实验,这就得改变C++的变参数机制,参数列表不能使用指针。C#和java使用数组可以很好地解决这个问题,因为所有的对象都可以通过object引用来传递,保持了原对象的类型信息,而在C/C++中是不可能的。如果哪天C++标准也定义一种所有类型的超父类型object的话,这个问题就迎刃而解了。
原文地址:
阅读(892) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~