浅析c++对象销毁之析构函数

在C++中,如果定义了对象,在内存中给它分配了空间;如果使用完以后,忘了销毁对象,即无法释放它所占用的内存空间。则这些内存空间一直被占用,直到重 启计算机才可释放,这样就造成了内存泄露。例:本来512Mb的内存,应该有300Mb的内存可用,可真正可以使用的内存只占100Mb,好像内存少了 200Mb一样。如果内存泄露比较严重,会造成系统性能降低,甚至崩溃。因此,为了减少内存泄露的发生,一定要注意对象的销毁问题。首先讨论一下C++中 对象的销毁。
3.    C++中对象的销毁
在C++中,普通的对象离开它的作用域之后,它的析构函数会被自动调用,从而销毁这个对象,释放它所占的内存,不会发生内存泄露的问题。
例:文件“point.cpp”
class Point
{
public:
       Point(int x=0,int y=0)
       {
               this->x=x; this->y=y;
              cout<<"Construct... x="<<",Y="<<Y<<ENDL;< P>
在这个程序中p1和p2是两个对象,它们的作用域就是从定义到它们所在的main()函数的结束。定义时自动调用构造函数,销毁之前会自动调用析构函数, 因此在构造函数和析构函数中加上一些输出语句,就可知道这个对象所占的空间是否被释放。在这个程序,构造了两个对象,并销毁了两个对象,所以不会产生内存 泄露。
从一个程序执行的开始到结束,如果一个对象只是调用了构造函数,而没有调用析构函数,即没有销毁这个对象,则会产生内存泄露。那么什么情况下可能产生内存泄露呢?
 
3.1 匿名对象
       如果我们把“point.cpp”中的main()函数中的语句变为
       void main()
{     new Point;    new Point(); new Point(3,3);  }
 
程序的运行结果是:


调用了三次构造函数,而没有调用析构函数,表明这三个对象只是在内存中动态分配了空间,但并没有销毁它们,没有释放它们的空间,造成了内存泄露。
 
3.2 用new动态分配空间时
用new命令可以为对象在内存中动态分配一定的空间,并返回分配空间的首地址;如果在程序运行结束之前没有用delete来销毁这些对象,释放它们所占用的空间也会发生内存泄露。
如果把“point”中的main()改为:
void main()
{
       Point *p1=new Point(1,1);
       Point *p2=new Point(2,2);
       delete p1;
}
程序的运行结果是:
浅析c++中的对象销毁


可以看到指针p1所指向的对象因为调用了delete,则调用了析构函数,而销毁了对象;而指针p2指向的对象只是调用了new,而没有调用delete,没有被销毁,也发生了内存泄露。
 
3.3 指针悬挂
例:“string.cpp”
#include
 
class String
{
public:
       String(int s){contents=new char[s]; }
       ~String()
       { cout<<"Deconstruct... "<< P>
private:
       char *contents;
       int size;
};
void main()
{
       String s1(10),s2(20);
       s1=s2;
}
在这个程序的main()函数中,创建了两个对象s1和s2,在创建时调用了它们的构造函数,为它们的数据成员contents分配了内存空间,并在执行 赋值语句s1=s2时,将对象s2的数据成员逐域复制到s1对象中去。即s1对象的指向字符串的指针contents不再指向原来的区域,而指向了s2的 字符串。这样,原来s1中的contents原来指向的区域就被封锁起来,没有办法释放,而这个区域也没有办法再用,产生了指针悬挂问题。而最后又使得一 块内存区域被释放了两次,这是又一个比较严重的问题。
指针悬挂也没有销毁对象,也产生了内存泄露。
 
C++的编程中,如果一个经常被调用的函数中出现了内存泄露现象。内存会一点点地被占用而不释放,到最后,系统的处理速度会越来越慢,严重影响系统的性能。要解决这个问题,只能通过重启计算机来完成,这样做的代价会很大。
例句一个完整的实例
#include <iostream.h>
class Mammal
{
public:
Mammal():itsAge(1) { cout << "Mammal constructor...\n"; }
//此处去掉~Mammal() 前的virtuall (1)
virtual ~Mammal() { cout << "Mammal destructor...\n"; }
virtual void Speak() const { cout << "Mammal speak!\n"; }
private:
int itsAge;
};


class Dog : public Mammal
{
public:
Dog() { cout << "Dog Constructor...\n"; }
~Dog() { cout << "Dog destructor...\n"; }
void Speak()const { cout << "Woof!\n"; }
};


void main()
{
Mammal *pDog = new Dog;
pDog->Speak();
//此处去掉delete pDog; (2)
delete pDog;


}
注意:
Java里没有类似C++中的动态内存申请和释放的机制,这是Java和C++的内存管理模型不同决定的。


在C++中,所有的普通变量(包括对象)都是声明的时候就获得内存,然后直到它所在的函数调用结束,它才消失,内存被释放。为了节约内存,才提出了动态内存申请的技术,可以在需要的时候再申请内存,不需要的时候再释放。内存泄露是C++里动态内存申请的问题。在Java里,因为用户无权管理内存,不存在因为程序员设计程序的原因引起的内存泄露,只存在由于Java虚拟机本身设计的问题引起的内存浪费。


Java中,对象全都是动态产生和释放的,声明一个对象时,它只是获得一个名字,并没有分配到内存。只有当使用new+构造方法实例化时,才会为它分配内存。而当这个对象不再使用时,Java的内存垃圾收集器会自动把它回收掉,这个是Java虚拟机自动执行的,不需要用户关心,更不需要显式地调用某个方法来删除对象。JAVA的对象自动进行内存计数,他的引用对象本身就包含了引用计数器。 当计数器归0时,对象自动释放。
所谓的finalize方法,是对象被垃圾收集器清除时自动调用的,而不是相反地通过调用它来清除对象。

你可能感兴趣的:(C++,delete,析构函数)