CPP

侯捷C++学习笔记:对象的创建和销毁

Posted by 蔡华的博客 on January 16, 2018

概述

  • 本文会记录CPP中对象的创建和销毁的过程中的细节

big three函数

  • 拷贝构造函数
  • 拷贝赋值函数
  • 析构函数

用途:这三个函数是在类中包含了指针的情况下使用的。

  • 当有一个成员变量是一个指针时,就有了给这个变量赋值的问题。如果这个赋值需要深拷贝,那么就需要申请内存空间。这样在析构函数中就需要对指针进行delete。
  • 这个成员变量可以是在对象构造函数中被赋值的,比如是个拷贝构造函数。也可以是在拷贝赋值函数中被赋值的。

对象创建

  • 使用Complex c1(1,2)这样的形式还是Complex* c2 = new Complex(1,2)这样的形式,都可以创建对象。
  • 构造函数不能直接被指针调用p->P::P(),这样写在某些编译器下可以通过编译但是某些不行。

new做了什么

image

生命周期

  • 对于c1这样在栈上的,生命周期就是C1的作用域。
  • 对于C2这样指针的,生命周期也看C2的生命周期,比如C2是个函数的局部变量,那么在函数结束时因为要调用delete c2,此时对象也就被销毁了。
  • 如果一个c3是static的,那么按照C里面的约定这个变量的生命周期就是进程的生命周期,不管这个C3是全局还是局部。

对象在内存中是什么样的布局

image

看图说话
  • 图是VC中的内存布局,一个对象的大小必须是16字节的倍数。
  • 灰色部分是debug模式下才有的,用于debug。
  • Release模式下一个对象包含上下hook共4*2=8个字节,对象本身的字节数(complex那个例子下就是实部和虚部共2个double,即4*2=8字节)。如果不够16的倍数就会出现墨绿色部分的pad,也就是填补内存。

对象的销毁

  • 生成的对象c1和c2都会在作用域结束后销毁。如果是c1那么它这个对象本身就在栈上保存,因此销毁后全部内存就释放掉了。但是指针的形式,因为对象是在堆上的,如果c2指针被销毁了,那么堆上这块内存就无法找到并销毁了(直到进程结束),造成了内存泄漏。
  • 因此对于指针形式的c2,在其使用完成后就可以直接调用delete c2来实现销毁。

析构函数

  • 如果一个对象中动态的分配了内存,那么需要在析构函数找那个对分配的内存进行删除。
  • 如果不显式的去写析构函数编译器应该会生成一个,如果我没有记错的话。
  • 上面的c1中如果是在栈上的,那么变量离开作用域后会自动销毁并调用析构函数。
  • 构造函数执行两个步骤,第一个执行

delete做了什么

image

数组删除

  • 如果动态生产了数组内存,则在析构函数或者当需要删除对象时需要用delete[]。在这个过程中具体干了什么呢?

image

  • 上面也说了,delete会先执行析构函数,其实对于一个对象来说,由于上下hook的存在因此析构函数可以完美的删除这个对象所占用的内存。所以图中左右两个delete都可以删除p所指向的数组,但是因为数组中是string的指针, 所以删除数组后string指针对指向的string对象本身并没有删除。而这个string对象本身其实就是char[]。这才是不用delete[]造成内存泄漏的本质。