1、智能指针的出现
在我们写程序的时候总会遇到一些需要new的问题,在没有智能指针的时候,我们只能收到的去delete,一旦我们忘记就会出现内存泄漏的问题。智能指针的出现就是为了解决这一问题,让我们new的对象,能够在使用完毕之后自己delete,而不用我们手动delete。
2、智能指针的历史
1、auto_ptr(C++98)
2、unique_ptr(C++11)
3、shared_ptr(C++11)
4、weak_ptr(C++11)
auto_ptr在C++98中提出在C++11中已经被抛弃,就不再叙述。
3、智能指针实现原理
智能指针是一种资源管理类,通过对原始指针进行封装,在资源管理对象进行析构时对指针指向的内存进行释放;通常使用引用计数方式进行管理。在指针进行拷贝的时候计数器会加1,而对一个对象进行赋值时,赋值操作符减少左操作数所指对象的引用计数(减1,如果引用计数为减至0,则删除对象),并增加右操作数所指对象的引用计数(加1)。
实现简单的智能指针:
class Counter
{
friend class SmartPointer;
public:
Counter()
{
ptr = NULL;
cnt = 0;
}
Counter(Object* p)
{
ptr = p;
cnt = 1;
}
~Counter()
{
delete ptr;
}
private:
Object* ptr;
int cnt;
};
class SmartPointer
{
public:
SmartPointer(Object* p)
{
ptr_counter = new Counter(p);
}
SmartPointer(const SmartPointer &sp)
{
ptr_counter = sp.ptr_counter;
++ptr_count->cnt;
}
SmartPointer& operator=(const SmartPointer &sp)
{
++sp.ptr_counter->cnt;
--ptr_counter->cnt;
if (ptr_counter->cnt == 0)
{
delete ptr_counter;
}
ptr_counter = sp.ptr_counter;
}
~SmartPointer()
{
- -ptr_counter->cnt;
if (ptr_counter->cnt == 0)
{
delete ptr_counter;
}
}
private:
Counter* ptr_counter;
};
4.1 unique_ptr
当我们定义一个unique_ptr的时候,需要将其绑定到一个new返回的指针。
只能有一个uniqu_ptr指向对象,也就是说它不能被拷贝,也不支持赋值,但是我们可以通过move来移动。
unique_ptr<string> p3 (new string ("test");
unique_ptr<string> p4;
p4 = p3;
上述代码是错误的。但是如说使用auto_ptr就是没有问题的
auto_ptr<string> p1(new string ("test") ;
auto_ptr<string> p2;
p2 = p1;
当我们使用auto_ptr时,可以有效的防止p2,p1指向同一对象,但是因为p2现在指向字符串,剥夺了了p1的权利,如此p1就不会指向任何对象,但是如果我们再使用p1的话,就会出现未知错误!使用unipue_ptr就可以很好的避免这一点,并且C++有一个标准库函数std::move(),让你能够将一个unique_ptr赋给另一个,使用move后,原来的指针仍转让所有权变成空指针,可以对其重新赋值。
unique_ptr<string> ps1, ps2;
ps1 = new string("test");
ps2 = move(ps1);
ps1 = new string("ok");
cout << *ps2 << *ps1 << endl;
而且,程序试图将一个 unique_ptr 赋值给另一个时,如果源 unique_ptr 是个临时右值,编译器允许这么做;如果源 unique_ptr 将存在一段时间,编译器将禁止这么做
unique_ptr<string> test(const char * str)
{
unique_ptr<string> temp (new string (str));
return temp;
}
unique_ptr<string> ps;
ps = demo('test");
4.2shared_ptr
采用引用计数的智能指针。 shared_ptr基于“引用计数”模型实现,多个shared_ptr可指向同一个动态对象,并维护了一个共享的引用计数器,记录了引用同一对象的shared_ptr实例的数量。当最后一个指向动态对象的shared_ptr销毁时,会自动销毁其所指对象(通过delete操作符)。shared_ptr的默认能力是管理动态内存,但支持自定义的Deleter以实现个性化的资源释放动作.
4.3weak_ptr
weak_ptr 提供对一个或多个 shared_ptr 实例所属对象的访问,但是,不参与引用计数。
5、循环引用
循环引用”简单来说就是:两个对象互相使用一个shared_ptr成员变量指向对方的会造成循环引用。
即A内部有指向B,B内部有指向A,这样对于A,B必定是在A析构后B才析构,对于B,A必定是在B析构后才析构A,这就是循环引用问题,违反常规,导致内存泄露。
解决循环引用方法:
1. 当只剩下最后一个引用的时候需要手动打破循环引用释放对象。
2. 当A的生存期超过B的生存期的时候,B改为使用一个普通指针指向A。
3. 使用weak_ptr打破这种循环引用,因为weak_ptr不会修改计数器的大小,所以就不会产生两个对象互相使用一个shared_ptr成员变量指向对方的问题,从而不会引起引用循环。