对象的构造和析构

对象的构造与析构

所谓有生即有死,对象有创建也该有销毁。又有所谓人有千奇百怪各有死法不同,对象的销毁也有很多种,不过不同于人的是,人的出生大致都相同的而死法却各自不同,对象的创建方法直接决定了它的销毁方法。

自动对象

最常见的自动对象,声明的时候在栈上创建,离开作用域就由系统自动销毁。在一个函数中咱们免不了声明几个局部变量,在函数结束的大括号那里,这些局部变量应该按什么顺序销毁呢?它们将按照构造的逆序被销毁。

自由存储对象

而自由存储对象,new的时候在堆上创建,delete的时候销毁。

类成员对象

作为别的类或者结构的成员的对象,它们的创建与销毁当然是随着作为容器的那个对象一起创建与销毁,但是它们也有自己的顺序。

首先,成员的构造函数,如果有的话,一定先于容器的构造函数被执行。这是一个自下而上的顺序。

第二,不管初始式列表怎么写,成员对象都是按照类里面声明成员的顺序来创建。这里就可能出现下面的问题:

class CBad

{

     public:

             int len;

             char* p;

       public:

            CBad(char*s):p(s),len(strlen(p)){}

            };

看上去似乎很完美,甚至编译也能通过但一运行就报错,很简单,构造函数的初始式按照声明的顺序创建成员对象,那么应该先运行len(strlen(p)),但p还没有初始化啊,肯定会出问题。所以最好的办法是将初始式列表的顺序与成员声明的顺序保持一致。

在销毁的时候,析构函数的调用就是一个自上而下的顺序,先调用容器类的析构函数然后才调用成员的析构函数。而成员销毁的顺序与构造时的顺序相反。

数组

数组的创建当然也是在声明的时候完成,例如

person people[100];

系统将自动调用100person的默认构造函数。然后在作用域结束时,系统再自动调用100次析构函数。而如果我们使用了new给数组分配空间,情况又不一样了。

person* p=new people[100];

构造函数仍然按照调用100次,而析构函数则必须显式调用,而且对于数组必须用delete[],这时系统将先调用100次析构函数,然后再销毁指针p释放内存。

局部静态对象

对于局部静态对象,控制线程第一次通过它的定义时调用构造函数。例如,在某个函数里面定义一个静态对象,第一次调用这个函数时将调用该对象的构造函数,但是在函数结束时,局部静态对象并不被销毁,而且下次如果再调用这个函数,也不会再调用静态对象的构造函数。

程序结束时,所有的静态对象将按照构造的逆顺序调用析构函数。

非局部对象

所谓非局部对象,一般是指全局变量、名字空间的变量、以及所有类的静态变量。在main()函数激活之前,这些对象就会完成构造。在同一编译单位中,所有的非局部变量按照它们声明的顺序构造,而在不同的编译单位中,构造顺序由编译器决定。

main()函数结束时,按照构造的逆顺序调用非局部对象的析构函数。

在这里存在一个问题,在某个文件里我们定义了一个静态对象,而在另一个文件里我们需要使用它,既然编译顺序不受控制,我们怎么能保证使用的是已经正确初始化的对象呢?这时候我们需要用到一个叫做第一次开关的东西,实际上就是一个静态bool flag,它能告诉我们某个对象是不是已经被初始化了。

临时对象

a=x+y*z的过程实际上是用yz的副本相乘,得到一个临时对象,比如说我们命名为_mul,然后再用x_mul的副本相加得到最终的结果,这仍然是一个临时对象,我们再给它起名为_sum,最后才是将_sum的副本赋给a

在这一长串的过程中,临时对象不断被悄悄地构造,然后等这个表达式结束时它们又悄悄地被销毁。整个过程无声无息,编译器工作得非常好,完全不需要我们操心。然而,总还是会出一些问题。

例如

string astr="hello";

    string bstr="world";

    constchar* cstr=(astr+bstr).c_str();

   cout<<cstr;

这样的写法并不罕见,但是非常危险。astr+bstr会得到一个临时对象,.c_str()将返回一个从这个临时对象抽取出来的C风格字符串,然后cstr指针指向它。看上去没什么问题,但是仔细一问就会出麻烦,既然astr+bstr是一个临时对象,那在这个表达式结束时它将被销毁,此时.c_str()返回的那个字符串还存在吗?说实话,第一次看到这个问题,我也紧张了好一下,因为从C#转过来的人谁不会写这种语句,可是一测试,居然能正常输出。看来编译器帮忙回避了这个问题,不过,谁又能保证每个编译器都能帮这个忙呢?

放置语法

new 出来的对象放在堆上,但是如果确实需要放在别的地方可以吗?万能的C++当然可以做到。

void*buf=reinterpret_cast<void*>(0xF00F);

    person*p=new(buf) person;

显然我们需要显式调用析构函数来销毁这个对象,还要想办法释放p指向的存储。所以,这种技术基本上就是错误的来源。


这是一篇介绍类构造和析构很好的文章,值得阅读。

文章来源:http://blog.sina.com.cn/s/blog_721f09800100ly70.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值