可能导致内存泄露的原因:
void Test()
{
int *p = new int(1);
if ()
{
return;
}
delete p;
}
此时,若执行if()语句,则指针p不能被释放,会造成内存泄漏。此过程为执行流跳转,可能会造成此过程的关键字有:return、break、continue、goto
若指针指向的空间可以自行释放,则不会出现这种问题。都知道,析构函数有这种功能,但是指针p知识一个常规指针,不是有析构函数的类对象指针。若为类对象指针,则在对象过期后,会自动调用析构函数释放指针指向的内存。这就是智能指针的基本思想。
实现智能指针
#include<iostream>
using namespace std;
template<class T>
class AutoPtr
{
public:
explicit AutoPtr(T *ptr)
:_ptr(ptr)
{}
AutoPtr(const T& ptr)
:_ptr(ptr)
{}
~AutoPtr()
{
cout << "AutoPtr()" << endl;
printf("0x%p", _ptr);
delete _ptr;
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
private:
T *_ptr;
};
void Func()
{
int *p1 = new int(2);
*p1 = 10;//解引用一个指针
cout << *p1 << endl;
AutoPtr<int> ap1(new int(1));
*ap1 = 10;
}
int main()
{
Func();
return 0;
}
常见的智能指针有:std::auto_ptr,boost::scopped_ptr,boost::shared_ptr,boost::scoped_array,boost::shared_array,boost::weak_ptr。
一、std::auto_ptr实现
#include<iostream>
using namespace std;
//AutoPtr
template<class T>
class AutoPtr
{
public:
//构造函数
explicit AutoPtr(T *ptr)
:_ptr(ptr)
{}
//拷贝构造
AutoPtr(AutoPtr<T>& ap)//此处要修改ap的对象成员,所以不能加const
{
_ptr = ap._ptr;
ap._ptr = NULL;
}
//赋值运算符重载
AutoPtr<T>& operator=(AutoPtr<T>&ap)
{
if (this != &ap)
{
if (_ptr)
{
delete _ptr;
}
_ptr = ap._ptr;
ap._ptr = NULL;//强行置空
}
return *this;
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
~AutoPtr()
{
printf("0x%p", _ptr);
delete _ptr;
}
private:
T *_ptr;
};
void TestAutoPtr()
{
AutoPtr<int>ap1(new int(2));
*ap1 = 10;
cout << *ap1 << endl;
AutoPtr<int>ap2(ap1);//AutoPtr<int>ap2=ap1
AutoPtr<int>ap3(new int(2));
ap3 = ap2;//赋值运算符重载
}
int main()
{
TestAutoPtr();
return 0;
}
ap1先将权限赋给ap2,两个指针指向同一块空间,ap2再将权限赋给ap3,三个指针指向同一块空间,调用析构函数时,三个对象都会调用析构函数,会导致一块空间释放三次,因此将实参的指针在结束后赋为空,与原意不符,因此舍弃。
二、boost::scoped_ptr实现
#include<iostream>
using namespace std;
template<class T>
class ScopedPtr
{
public:
//构造函数
ScopedPtr(T *ptr)
:_ptr(ptr)
{}
//析构函数
~ScopedPtr()
{
if (_ptr)
{
delete _ptr;
}
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
private:
//只声明不实现
ScopedPtr(const ScopedPtr<T>& sp);
ScopedPtr<T>& operator=(const ScopedPtr<T>& sp);
private:
T *_ptr;
};
void TestScopedPtr()
{
ScopedPtr<int>sp1(new int(1));
*sp1 = 10;
cout << *sp1 << endl;
//ScopedPtr<int>sp2(sp1);// 拷贝构造失败
//ScopedPtr<int>sp3(new int(2));
//sp3 = sp1;//赋值运算失败
}
int main()
{
TestScopedPtr();
return 0;
}
ScopedPtr指针通过将类中的拷贝构造函数以及赋值运算符重载函数声明,并不进行实现,为了不让使用者在后期使用的时候进行更改,将其放在private中。简单粗暴。这种方法称为防拷贝。
三、boost::shared_ptr
#include<iostream>
using namespace std;
template<class T>
class SharedPtr
{
public:
SharedPtr(T* ptr)
:_ptr(ptr)
, _refCount(new int(1))
{}
SharedPtr(const SharedPtr<T>& sp)
{
_ptr = sp._ptr;
_refCount = sp._refCount;
(*_refCount)++;
}
SharedPtr<T>& operator=(SharedPtr<T>& sp)
{
if (_ptr != sp._ptr)
{
if (--(*_refCount) == 0)
{
delete _ptr;
delete _refCount;
}
_ptr = sp._ptr;
_refCount = sp._refCount;
(*_refCount)++;
}
return *this;
}
~SharedPtr()
{
if (--(*_refCount) == 0)
{
printf("0x%p", _ptr);
delete _ptr;
delete _refCount;
}
}
T& operator*()
{
return *_ptr;
}
T* operator->()
{
return _ptr;
}
private:
int *_ptr;
int *_refCount;
};
void TestSharedPtr()
{
SharedPtr<int> sp1(new int(10));
*sp1 = 10;
SharedPtr<int> sp2(sp1);
cout << *sp1 << endl;
cout<<*sp2<<endl;
SharedPtr<int> sp3(new int(1));
sp3 = sp2;
cout << *sp3 << endl;
}
int main()
{
TestSharedPtr();
return 0;
}
在每个类中添加一个引用计数,有几个对象指针指向同一块内存空间,则这块内存空间的引用计数就是几,当引用计数为0时,这块空间就需要释放,当其中一个对象指针被赋予其它内存空间是,这个指针原先对应的那块空间的引用计数就需要减1。
总结
早期C++98
auto_ptr ----管理权转移
boost非官方的库
scoped_ptr ----守卫指针
shared_ptr ----共享指针
weak_ptr ----弱指针(不能单独存在)
C++11(将boost中的指针引入了标准库中)
unique_ptr
shared_ptr
weak_ptr