在C/C++中我们malloc/new申请的内存都要自行释放,除非程序结束,否则会造成内存泄漏,这么久以来分配释放已经成为一个习惯,但是有时候还是会有疏漏,忘记释放。而C++中有这么一类东西,它可以替我们释放不再使用且未手动释放的内存,这种所谓的东西就叫做智能指针。而智能指针有多种(std::auto_ptr、boost::scoped_ptr、boost::shared_ptr、boost::scoped_array、boost::shared_array、boost::weak_ptr、boost::intrusive_ptr)今天我们就看看最简单的一种:std::auto_ptr以及最基本的new、delete等运算符的重载基础。
1、C++中new、delete对于内存的管理:
通常new与delete是不可分离的,举例说明:
#include <iostream>
using namespace std;
class A{
public:
A(){cout<<"A()";}
~A(){cout<<"~A()";}
void func(){
cout<<"I'am function in class A"<<endl;
}
private:
int data;
};
int main()
{
A * a = new A[5];
delete []a;
A * b = new A;
delete b;
return 0;
}
对于该例来说: new与new[]的空间我们均得各自释放,在程序的执行过程中,先构造a[5],即先调用5次构造器,再调用5次析构器。在a[5]回收以后接着调用一次构造器产生b对象,最后delete b;调用析构器并释放b对象空间。
结果如下:
A()A()A()A()A()~A()~A()~A()~A()~A()A()~A()
但是当我们不调用deelte时,则只输出六个A()而不会输出~A()。就是因为程序不能自动调用析构器析构对象释放内存。那么我们采用智能指针的方式作以修改。
2、new、delete重载:
(1)、new、delete的重载
new、new[]、delete、delete[]都可以重载,且有[]与无[]是不一样的,其原型基本是固定的:
void *operator new(size_t);
void operator delete(void *);
void *operator new[](size_t);
void operator delete[](void *);
我们来测试一下重载后的运算符在调用过程中发生了什么:
#include <iostream>
#include <stdlib.h>
#include <string.h>
using namespace std;
class A{
public:
A(){cout<<"A()"<<endl;}
~A(){cout<<"~A()"<<endl;}
void func(){
cout<<"I'am function in A"<<endl;
}
/*非全局即定制化的,只在new A时调用,即只对A的堆中对象申请有效*/
void * operator new(size_t size){
cout<<"size = "<<size<<endl;
cout<<"operator new()"<<endl;
void * p = malloc(size);
memset(p, 0, size);
//((A*)p)->data = 1;//定制化形式的初始化
return p;
}
void operator delete(void * p){
cout<<"operator delete()"<<endl;
free(p);
}
private:
int data;
};
/*全局即非定制化的,即使是new int[1]也会调用重载后的*/
void *operator new[](size_t size)
{
cout<<"size = "<<size<<endl;
cout<<"operator new[]()"<<endl;
void * p = malloc(size);
memset(p, 0, size);
return p;
}
void operator delete[](void * p)
{
cout<<"operator delete[]()"<<endl;
free(p);
}
int main()
{
A * a = new A[5];//调用的重载new[]会自动调用构造函数
//delete a;//delete a只会析构一个对象,不会析构整个对象数组
delete []a;
A * b = new A;
delete b;
return 0;
}
结果如下:
3、智能指针智能管理:
了解了new与delete的重载以后,我们再来学习智能指针以及部分原理,将示例修改如下(该例分为三次测试,第一部分即func1()测试普通的new与delete,第二部分即func2()测试系统STL模板提供的智能指针auto_ptr,第三部分即func3()测试我们自己简单实现的智能指针):
#include <iostream>
#include <memory>
using namespace std;
class A{
public:
A(){cout<<"A()"<<endl;}
~A(){cout<<"~A()"<<endl;}
void func(){
cout<<"I'am function in A"<<endl;
}
int data;
};
class Smart{//智能指针的简单实现,对于智能指针的操作也就是对对象的操作
public:
//接收对象指针并赋给ptr,那么对ptr的操作就是对A对象的操作
Smart(A*p)
:ptr(p){}
~Smart(){
delete ptr;
}
/*每一种智能指针都需要重载->与*运算符,我们实现的也自然不例外*/
A *operator->(){//重载->
return ptr;
}
A& operator *(){//重载*
return *ptr;
}
private:
A * ptr;
};
void func1()
{
A * p = new A;/*申请*/
delete p;/*对应释放*/
}
void func2()
{
auto_ptr<A> ptr(new A);
(*ptr).func();
ptr->func();//ptr为对象,其行为表现的像一个指针,是因为重载了->和.
}
void func3()
{
Smart smt(new A);
smt->func();//需要重载->
(*smt).func();//需要重载*
}
int main()
{
func1();
cout<<endl;
func2();
cout<<endl;
func3();
return 0;
}
测试结果:
我们可以看到我们简单实现的Smart Point与模板库提供的Smart Point:auto_ptr基本功能已经相差无几,都对->和*进行了重载。
注意: auto_ptr为类模板,auto_ptr<A>
为模板类; 也就是说auto_ptr对new的内存进行智能指针的对象传参,操作ptr对象也就相当于操作A的对象,并且delete在ptr离开func函数的栈空间的时候发生。是由封装好的智能指针的类的对象在析构时其析构函数完成的(因为Smart类对象在栈区,Smart类对象为自行析构),而不是凭空会回收的。