个人对智能指针的理解
智能指针类,方便内存的管理。对于一般指针,空间被new出来后,需要显式地调用delete从而释放空间,否则就会有内存泄漏的问题。而智能指针的智能正是体现在这一方面,它不需要显式地调用delete,从而提高代码的容错率,减少了内存泄漏的可能。
智能指针实质上是一个对象,但是表现却像一个指针,正常情况下,可以使用指针的->和*操作运算符。
个人认为,智能指针的实质是用处于栈区的对象能自动消亡的特性来释放处于堆区的对象。
常见的几种智能指针
auto_ptr(已被摒弃)
使用案例:
#include<iostream>
#include<memory>
using namespace std;
class myclass
{
public:
myclass() {
cout << "class is new" << endl;
};
~myclass()
{
cout << "class is over" << endl;
system("pause");
}
void print()
{
cout << "now printing" << endl;
}
};
int main()
{
auto_ptr<myclass> ptr(new myclass());
ptr->print();
//myclass *ptr = new myclass;
system("pause");
return 0;
}
当没使用auto_ptr时,对象被一般指针指向,此时new出来的东西很明显不会被释放,产生内存泄漏。当使用auto_ptr 智能指针指向 myclass类时,程序执行结果如下所示
可得,该对象被正常释放,智能指针起作用。
auto_ptr被摒弃的主要原因
请看下面代码:
int main()
{
auto_ptr<myclass> ptr(new myclass());
ptr->print();
auto_ptr<myclass> ptr2 = ptr;
ptr->print();
system("pause");
return 0;
}
其他的不变,我多定义了一个ptr2 = ptr;再次运行程序
是的,出现了对野指针的访问。这是因为在ptr2 = ptr;这一步,ptr把对myclass指针的所有权给到了ptr2,而自己的ptr.get()被置为空。
在代码中这是极为不安全的操作,理论上auto_ptr智能指针不该调用拷贝构造函数对对象进行创建才对,这就是该智能指针被抛弃的原因。
来看看auto_ptr的升级版,unique_ptr智能指针
int main()
{
unique_ptr<myclass> ptr(new myclass());
ptr->print();
unique_ptr<myclass> ptr2 = ptr;
cout << ptr.get();
system("pause");
return 0;
}
同样的代码,编译一下。
报错了,编译器智能检测,拷贝构造函数无法被调用,从根本上防止了第二个尝试指向同一个指针的对象被创建。
如果一个参数有一个形参是 unique_ptr,该如何传参?
函数定义如下
void fun(unique_ptr<myclass> p) {}
传参方式如下
fun(std::move(ptr));
直接传ptr会报错。
shared_ptr智能指针
上面介绍的unique_ptr类智能指针只能有一个对象指向需要的指针,有些场合还是需要多个智能指针对象同时指向某块内存。例如多线程操作某块内存时,使用unique_ptr可能会导致那块内存空间被提前释放,出现意想不到的错误。所以就有了shared_ptr智能指针。
int main()
{
shared_ptr<myclass> ptr(new myclass);
shared_ptr<myclass> ptr2 = ptr;
shared_ptr<myclass> ptr3 = ptr;
ptr->print();
system("pause");
return 0;
}
该智能指针通过内部的一个count计数器进行计数,当删除一个智能指针时,并不影响其它同类智能指针的继续使用。因为该片内存添加了一个引用计数,每shared_ptr一次,引用计数+1;每次调用析构函数,引用计数减一。直到最后一个智能指针删除,才会释放内存。
shared_ptr智能指针的循环引用
产生的原因:A对象中有一个成员是指向B的shared_ptr B>智能指针,同时B中也有一个指向A的shared_ptrA>智能指针。
class A
{
public:
A() { cout << "parent new" << endl; }
~A() {
cout << "parent delete" << endl;
system("pause");
}
shared_ptr<B> ptr;
};
class B
{
public:
B() { cout << "child new" << endl; }
~B() {
cout << "child delete" << endl;
system("pause");
}
shared_ptr<A> ptr;
};
int main()
{
shared_ptr<A> parent(new A());
shared_ptr<B> child(new B());
child->ptr = parent;
parent->ptr = child;
system("pause");
return 0;
}
这就有了:
对象A何时被释放?:当对象A的引用计数为0的时候对象A被释放,所以对象A的内存释放离不开对象B中那个指向A的智能指针消亡,即对象B消亡。
同样的,对象B何时被释放?:当引用对象B的智能指针引用计数为0时,对象B的内存空间被释放,
所以对象A的内存释放离不开对象A中那个指向B的智能指针消亡,即对象A消亡。
A的消亡要建立在B消亡基础上,B的消亡要建立在A基础上,这就陷入了死循环,导致了内存泄漏。
如何有效避免这一现象?使用weak_ptr指针
介绍:weak_ptr智能指针是一种辅助类指针,需要与一个shared_ptr智能指针绑定,weak_ptr可以通过自身的lock()函数使用shared_ptr指针的内存空间,且不增加引用计数,就可用于上面的情况,在B中将shared_ptr转成weak_ptr类。
weak_ptr<A> ptr = B.ptr;
weak_ptr 通过expired()来判断辅助的shared_ptr指针内存空间有无被释放。