`
ihuashao
  • 浏览: 4513812 次
  • 性别: Icon_minigender_1
  • 来自: 济南
社区版块
存档分类
最新评论

C++对象是怎么死的?关于标准输入输出流(cout,cerr,clog,etc)的进一步探讨

阅读更多

  昨天的帖子“C++ 对象是怎么死的?进程篇 ”里面,谈到全局对象的析构顺序,当时我举了一个“在析构函数中使用cout”的例子(详见原文 )。我当时的本意是想说明:全局对象的析构顺序是不确定的,最好不要在程序逻辑中依赖这个顺序(免得出现移植问题)。
   没成想该帖子引来热烈的评论(我有点受宠若惊了),关注的焦点主要集中在:“cout是否会最后析构”。有些网友质疑我所提到的有关VC6的行为;有网 友引用了TC++PL上的相关章节(21.5.2)来说明“cout会最先构造、最后析构”。既然大伙儿对标准流类库的构造和析构挺有兴趣,咱们今天就针 对这个话题补充说明一下。

  ★标准都说了些啥?
  C++ 03标准的18.3章节提到了进程启动和终止。其中对进程的自然死亡有比较具体的描述,包括:各种类型的对象啥时候销毁、用atexit 注册的退出函数啥时候被调用、还有输入输出流啥时候flushclose 等等。
  另外,在27.4.2.1.6章节中阐述了ios_base::Init 如何通过包含一个引用计数,对八个标准流类库的全局对象进行适当的初始化和销毁。
  那么,有了C++ 03标准作书面担保,我们是否就高枕无忧呢?

  ★关于老式编译器(03年之前推出)
  由于老式编译器在标准发布之前推出,因此对标准的实现不够好(这不是废话嘛)。关于老式编译器的标准兼容性,在“C++移植性和跨平台开发[2] ”有进一步说明。
  老式编译器的典型代表就是VC++ 6.0。其实已经有很多人碰到了“VC6的cout提前析构”的问题,不信大伙儿可以在Google上搜一下。另外,如果你手头上正好有VC6,可以试试附在本文末尾的示例1的代码,看是否能够正常打印,就知道了。
  当然啦,VC6可以称得上是古董级的编译器了,从98年推出到现在,已经超过10个年头。所以它对标准的实现不好也无可厚非。

  ★关于新式编译器(03年之后推出)
   那么新式编译器是否就完美地支持C++ 03标准呢?我感觉有点玄。所以今天特地试验了手头能找到的几个C++编译器。看看它们是否能够保证cout最先构造、最后析构。另外,为了给这些新式编 译器增加点挑战,我把上述的示例1代码稍微修改了一下,成为示例2代码(说实话,这么写代码确实有点夸张),也附在本文末尾。
  手头的几种编译器测试下来,结果如下:
----------------------------
  操作系统 编译器版本      示例1  示例2
  Win2000  VC 7.1       OK   OK
  RHEL3   GCC 3.2.3      OK   OK
  Win2000  GCC 3.4.2(MingW)  OK   启动时崩溃
  Win2003  GCC 3.4.4(Cygwin)  OK   启动时崩溃
----------------------------
  对于示例2代码造成的崩溃,经过简单排查,基本可以推断是cout滞后构造导致(也就是用到cout时,它还没生出来)。而且GCC 3.4.2版本是2004年出品的,应该不算太老啊(至少03标准已经发布一年了)。从上面的结果看,Linux上版本较老的GCC反而没有问题。所以我猜测 或许GCC在Windows上的移植版本有这个缺陷(仅仅是猜测啊)。
  由于时间关系,没来得及深入研究。如果有同学不信,可以找个类似的环境试验一下(没准儿最后发现是我搞错了,也有可能哦)。另外,想打破砂锅的同学,可以去琢磨一下出问题的编译器,看看它们的内部实现。

  ★总结
  根据上述的情况,我个人建议:如果你的代码有全局对象,并且你的代码可能 会跨编译器,那就避免在全局对象的构造函数和析构函数中使用标准流类库的那八个玩意儿(包括cout、cerr、clog等)。
  毕竟这八个全局对象,都有对应的标准C替代品,并不是不可替代的嘛。大伙儿犯不着冒险嘛。如果你确实舍不得流式操作符(<<和>>)或者确实看不惯printf 的变参,你可以用字符串流先格式化好,再用标准C的函数输出嘛(也就多一两行代码而已嘛)。

  最后附上示例代码,供有兴趣的同学尝试。大伙儿如果有新的发现,欢迎发评论告诉我。


// ========示例1代码========
#include <iostream>
using namespace std;

class CFoo
{
public:
CFoo()
{
cout << "CFoo" << endl;
}

~CFoo()
{
cout << "~CFoo" << endl;
}
};

CFoo g_foo;

int main()
{
return 0;
}

// ========示例2代码========
class CFoo
{
public:
CFoo();
~CFoo();
};

CFoo g_foo;

#include <cstdio>
#include <iostream>
using namespace std;

CFoo::CFoo()
{
puts("puts CFoo");
cout << "cout CFoo" << endl;
}

CFoo::~CFoo()
{
cout << "cout ~CFoo" << endl;
puts("puts ~CFoo");
}

int main()
{
return 0;
}

版权声明
本博客所有的原创文章,作者皆保留版权。转载必须包含本声明,保持本文完整,并以超链接形式注明作者编程随想 和本文原始地址:

http://program-think.blogspot.com/2009/02/cxx-object-destroy-with-io-stream.html

分享到:
评论

相关推荐

    C++ 输入输出流重定向到外设

    C++ 标准输入输出模块,为字符流操作提供了便捷的途径,软件开发当中,尤其是嵌入式系统开发当中,有时候需要把流信息重新定向到特定的端口,如串口,以太网,USB等。如标准输入输出cout, cin默认将字符流定向到...

    C++ 基本的输入输出cout

    C++ 基本的输入输出 C++ 标准库提供了一组丰富的输入/输出功能,我们将在后续的章节进行介绍。本章将讨论 C++ 编程中最基本和最常见的 I/O 操作。 C++ 的 I/O 发生在流中,流是字节序列。如果字节流是从设备(如...

    C++格式输入输出

    介绍C++的格式输入输出

    C++标准库stl

    &lt; iostream&gt; 支持标准流cin、cout、cerr和clog的输入和输出,它还支持多字节字符标准流wcin、wcout、wcerr和wclog。 &lt;iomanip&gt; 提供操纵程序,允许改变流的状态,从而改变输出的格式。 &lt;ios&gt; 定义iostream的基类 ...

    C/C++输出彩色文字printf和cout显示的文字是彩色的有颜色的

    C/C++输出彩色文字printf和cout显示的文字是彩色的有颜色的

    C++输入输出流实验

    #include #include using namespace std; int main() {float a,b,c,s; cout输入a,b,c的值"; cin&gt;&gt;a&gt;&gt;b&gt;&gt;c; s=(a+b+c)/2; if(a+b&gt;c&&a+c&gt;b&&b+c&gt;a) cout(s*(s-a)*(s-b)*(s-c));...cerr输入数据有误"; return 0; }

    C++输入输出流.pdf

    C++第10章 输入输出流 大量输入输出流实例分析 适合初学者学习

    C++键盘输入与屏幕输出

    键盘输入与屏幕输出 C++中通过输入/输出流来实现标准输入/输出操作。 流是与I/O设备相关联的数据通信对象 输入操作通过流cin来实现 输出操作通过流cout来实现

    C++讲解:孙鑫之掌握C++.md

    ​ C++中提供了一套输入输出流类的对象,它们是**cin 、cout和cerr**,对应c语言中的三个文件指针stdin、stdout、stderr,分别指向终端输入、终端输出和标准出错输出(也从终端输出)。**cin与&gt;&gt;一起完成输入操作,...

    C++输入输入 cin cout

    c++的输入输入教程源码,非常实用,而且都是经过测试的。

    C++--IO流类库

    标准输出流 cout 与标准输出设备相关联 非缓冲型标准出错流 cerr 与标准错误输出设备相关联(非缓冲方式) 缓冲型的标准出错流 clog 与标准错误输出设备相关联(缓冲方式) 在默认情况下,指定的标准输出设备是显示终端...

    C++输入输出流

    为了更好的学习c++的朋友,这里推荐一篇输入输出流部分的文档,其类容讲的深入透彻,利于学习。

    C++语言常用的流输入输出

    C++语言常用的流输入输出,包含有格式及无格式输出、流输入及流输出

    C++ 中cerr和cout的区别实例详解

    C++ 中cerr和cout的区别实例详解 前言:  cerrThe object controls unbuffered insertions to the standard error output as a byte stream. Once the object is nstructed, the expression cerr.flags & unitbuf ...

    《由浅入深学C++-基础、进阶与必做300题》pdf格式 (完整版 带目录

    4.3.3 输出流cerr和clog 68 4.4 格式控制函数 69 4.5 格式控制符 71 4.5.1 控制不同进制的输出 72 4.5.2 控制输出宽度 72 4.5.3 控制输出精度 73 4.6 顺序结构综合应用 74 4.7 小结 75 4.8 习题 75

    C++的输入和输出

    C++语言本身不提供输入与输出的操作,但是可以使用标准库提供的输入与输出功能,即程序中独立于设备的 I/O 操作。 输入与输出包括: ...C++的输入输出流可以看作外部设备和计算机内存之间流动的字节序列,这些字

    详解C++ cout格式化输出完全攻略

    写算法题的时候突然发现自己忘记...C++ 中的 cout 对象则使用流操作算子(你也可以叫做格式控制符)或者成员函数进行控制。 使用流操作算子 C++ 中常用的输出流操纵算子如表 1 所示,它们都是在头文件 iomanip 中定义的

    C++对象和指针的引用

    从输出结果可以看出,当在被调用函数fun中,改变了对象的数据成员值[m1.setxy(12, 15)]和指向对象指针的数据成员值[m2-&gt;setxy(22, 25)]以后,可以看到只有指向对象指针作参数所指向的对象被改变了,而另一个对象作...

    C++中Cout的详细实现介绍

    详细介绍了C++中,cout的实现方式。

Global site tag (gtag.js) - Google Analytics