设为首页 加入收藏

TOP

C++ ostream与printf比较
2012-11-28 12:56:30 来源: 作者: 【 】 浏览:316
Tags:  ostream printf 比较

    C++(www.cppentry.com)标准库,可以说为了保证其通用性和效率,真是绞尽脑汁、C++(www.cppentry.com)特性无所不用其极,而且还专门为了设计容器增加了模板的支持。C++(www.cppentry.com)的流当然也不是十全是美,这也跟C++(www.cppentry.com)的特性本身有关,还谈不上“糟糕”,更不能说“很糟糕”!

    原因在于

    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<str(); // 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(“%u”, i):

    使用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++(www.cppentry.com)流设计有缺陷,极其推崇GCC的__attribute__检查制机制,但事实证明它并不能解决问题,对于“%u”输出格式不能保证数据正确个输出,而且不能给出警告,所以很容易导致输出数据的错误。如果想彻底解决问题,通过C++(www.cppentry.com)标准,严格定义格式输出,并在编译期进行强类型检查,这但样做不太合适,至少不适合定义到编译器的标准中去,因为printf只是一个库函数一样,语言不可能因为某个函数制定标准。 boost::format和fastformat也都是通过格式字符串来解析,无论功能如何强大,都不可能在编译器层面解决类型检查的问题,输出错误也就很难避免。

    流操作符只是一个普通的运算符,它不可能预测未来如何使用它,不可能有办法在语法上强制用户多调用它一次,不仅C++(www.cppentry.com)做不到,任何一种语言也都不可能做到。如果要彻底解决格式化的问题,但是可以通过RTTI来实验,这就得改变C++(www.cppentry.com)的变参数机制,参数列表不能使用指针。C#和java使用数组可以很好地解决这个问题,因为所有的对象都可以通过object引用来传递,保持了原对象的类型信息,而在C/C++(www.cppentry.com)中是不可能的。如果哪天C++(www.cppentry.com)标准也定义一种所有类型的超父类型object的话,这个问题就迎刃而解了。

】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇C++ 类访问控制小结 下一篇poj3254 Corn Fields--..

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容: