四个智能指针,auto_ptr, unique_ptr, shared_ptr, weak_ptr,其中
后三个是C++11支持,第一个已经启用。
auto_ptr缺点:
- 两个auto_ptr指向同一个内存的话会造成多次析构释放。
- 采用“=”赋值语句的时候,会将右边的所指向的内存转移到左边的智能指针上,这样原来的右边的智能指针就不再指向原来那块内存了。
- 当他作为参数时,会将实参的内存转移到形参上,这样实参也无法找到原来的那块内存了。
使用普通指针,容易造成堆内存泄露(忘记释放),二次释放,程序发生异常时内存泄露等问题等,使用智能指针能更好的管理堆内存。
#include <iostream>
#include <memory>
using namespace std;
class A: public enable_shared_from_this<A>
{
public:
shared_ptr<A> GetSelf() {
//return shared_ptr<A>(this); // 不要这么做
return shared_from_this();
}
~A(){
cout << "delete A\n" << endl;
}
};
class B;
class C;
class B{
public:
//shared_ptr<C> cptr;
weak_ptr<C> cptr;//改为weak
int *val;
B() {
val = new int;
}
~B() {
cout << "delete B" << endl;
delete val;
}
};
class C{
public:
shared_ptr<B> bptr;
~C() {
cout << "delete C" << endl;
}
};
void deletePtr(int *p) {
cout << "call delete" << endl;
delete p;
}
void test() {
shared_ptr<A> ap(new A);
weak_ptr<A> wp1 = ap;
weak_ptr<A> wp2 = ap;
cout << "ap.use_count()" << ap.use_count() << endl;
}
void test2() {
shared_ptr<A> wp;
{
shared_ptr<A> ap(new A);
wp = ap;
}
cout << "wp.use_count()" << wp.use_count() << endl;
if(!wp.expired()) {
//wp不能直接操作对象的成员和方法,需要先lock获取shared
shared_ptr<A> ptr = wp.lock();
*(ptr->val) = 20;//资源已经被释放了
}
}
int main()
{
{
A *pa = new A;//没有调用delete对指针进行释放导致内存泄漏。
std::shared_ptr<A> pb(new A);//智能指针,加强内存释放问题。
}
{
/*
* pb,pb1,pb2都指向的是同一块内存
* shared引入了一个计数机制,每个引用一个就加一操作
* 当引用计数降为0才释放。
* 不要用地址传递的方式。
* 不能通过直接将原始这种赋值来初始化,需要通过构造函数和辅助方法来初始化。
* 应该先使用make_shared来构造智能指针,因为更高效。
* 通过get方法来获取原始指针,但不推荐使用
* 如果用shared_ptr管理非new对象或没有析构函数的类时,应为其传递合适的删除器。
* shared_ptr实现包含了两部分,
一个指向堆上创建的对象的裸指针,raw_ptr
一个指向内部隐藏的、共享的管理对象。share_count_object
* 对于一个未初始化的智能指针,可以通过reset方法
* 来初始化,当智能指针有值的时候调用reset会引起引用计数减1。
* 智能指针可以通过重载的bool类型操作符来判断。
* 不要保存p.get()的返回值 ,无论是保存为裸指针还是shared_ptr都是错误的
* 保存为裸指针不知什么时候就会变成空悬指针,保存为shared_ptr则产生了独立指针
* 不要delete p.get()的返回值 ,会导致对一块内存delete两次的错误
*/
shared_ptr<A> pb(new A);
shared_ptr<A> pb1 = pb;
shared_ptr<A> pb2 = pb;
{
//shared_ptr<int> p = new int(1);//不能将一个原始指针直接赋值给一个之恩那个指针。
}
shared_ptr<int> p1;
p1.reset(new int(1)); //分配资源
shared_ptr<int> p2 = p1;//此时p2.use_count()打印出的应该是2
p1.reset(); //没有参数就是手动释放资源。此时打印p2.use_count()打印出的是1
if(!p1) {//此时p1是空的
cout << "p1 is empty\n" << endl;
}
if(!p2) {//p2非空
cout << "p2 is empty\n" << endl;
}
p2.reset();
if(!p2) {//p2空
cout << "p2 is empty\n" << endl;
}
}
shared_ptr<int> p(new int(1), deletePtr);
//使用匿名函数的方式
shared_ptr<int> b1(new int(1), [](int *p) {
cout << "call" << endl;
delete p;
});
shared_ptr<int> b2(new int[1], [](int *p) {
cout << "call" << endl;
delete []p;
});
cout << "Hello World!" << endl;
auto sp1 = make_shared<int>(100);//相当于shared_ptr<int> sp1(new int(100));
shared_ptr<int> ptr(new int(1));
int *e = ptr.get();
/*
* 不要用一个原始指针初始化多个shared_ptr
* int *ptr = new int;
* shared_ptr<int> p1(ptr);
* shared_ptr<int> p2(ptr);
*
* 不要在函数实参中创建shared_ptr
* function(shared_ptr<int>(new int), g());
* 因为C++的函数参数的计算顺序在不同的编译器不同的约定下可能是不一样的,一般是从右到左,但也
可能从左到右,所以,可能的过程是先new int,然后调用g(),如果恰好g()发生异常,而shared_ptr还
没有创建, 则int内存泄漏了,正确的写法应该是先创建智能指针.
*通过shared_from_this()返回this指针。不要将this指针作为shared_ptr返回出来,因为this指针
本质上是一个裸指针,因此,这样可能会导致重复析构。
* 通过shared_from_this()返回this指针。不要将this指针作为shared_ptr返回出来,因为this指针
本质上是一个裸指针,因此,这样可能会导致重复析构。
*/
/*
* 测试this
*/
shared_ptr<A> st1(new A);
shared_ptr<A> st2 = st1->GetSelf();//执行段错误,会有二次析构
cout << "st1--------->" << st1.use_count() << endl;
//结果都为1,显然是错误的。由于用同一个指针(this)构造了两个智能指针sp1和sp2,而他们之间(智能指针)是没有任何关系
//的,在离开作用域之后this将会被构造的两个智能指针各自析构,导致重复析构的错误。
//正确返回this的shared_ptr的做法是:让目标类通过std::enable_shared_from_this类,然后使用基类的
//成员函数shared_from_this()来返回this的shared_ptr,修改之后结果为2
cout << "st2--------->" << st2.use_count() << endl;
/*
* 避免循环引用。循环引用会导致内存泄漏
*/
shared_ptr<B> b(new B);
shared_ptr<C> c(new C);
b->cptr = c;
c->bptr = b;
b->cptr.reset();
//在程序结束之后没有调用B和C的析构函数,需要手动释放
//因为他们循环引用,导致指针不为0,产生内存泄漏,解决办法:将B和C任何一个成员变量改为weak_ptr
/*
* unique_ptr:独占型智能指针,不允许其他智能指针共享内部指针,
* 不允许通过赋值将一个unique_ptr赋值给另一个智能指针。
* unique_ptr<T> my_ptr(new T);
* unique_ptr<T> my_other_ptr = my_ptr;//报错不能赋值
* 但是它可以通过函数返回给其他的智能指针,还可以通过move来转移到其他指针上。
* unique可以指向一个数组,但是shared不行
* unique_ptr<A[]> ptr(new A[10]);正确
* shared_ptr<A[]> ptr2(new A[10]);错误
* unique的指定删除器和shared_ptr也不一样
* shared_ptr<int> ptr3(new int(1), [](int* p){delete p;});正确
* unique_ptr<int> ptr4(new int(1), [](int* p){delete p;});错误
* unique需要确定删除器的类型
* unique_ptr<int, void(*)(int*)> ptr5(new int(1), [](int* p){delete p;});正确
* 总之,unique和shared区别:
* 1. unique独占不能赋值,只能转移
* 2. unique可以指向数组,shared不可以
* 3. make_unique出现在C++14,make_shared出现在C++11
* 4. unique删除器需要确定类型
*/
unique_ptr<A> my_ptr(new A);
unique_ptr<A> my_other_ptr = move(my_ptr);//这是my_ptr变为空
/*
* C++14中提出了make_unique
*/
/*
* weak_ptr:weak_ptr 是一种不控制对象生命周期的智能指针, 它指向一个 shared_ptr 管理的对象.
* 进行该对象的内存管理的是那个强引用的shared_ptr, weak_ptr只是提供了对管理对象的一个访问手段
* 它只可以从一个shared_ptr 或另一个 weak_ptr 对象构造, 它的构造和析构不会引起引用记数的增加或减少
* 通过use_count()方法获取当前观察资源的引用计数
* 通过expired()方法判断所观察资源是否已经释放
* 通过lock方法获取监视的shared_ptr
* 不能直接将this指针返回shared_ptr,需要通过派生
std::enable_shared_from_this类,并通过其方法shared_from_this来返回指针,原因是
std::enable_shared_from_this类中有一个weak_ptr,这个weak_ptr用来观察this智能指针,调用
shared_from_this()方法是,会调用内部这个weak_ptr的lock()方法,将所观察的shared_ptr返回
* weak_ptr使用注意事项:
* weak_ptr在使用前需要检查合法性。
*/
auto uni(make_unique<A>());
test();
/*
* 智能指针安全问题:
* shared_ptr安全:引用计数本身是安全的,多线程下,复制几次就加几次。比如参数使用的是值传递
* shared_ptr不安全:不同线程如果共用一个shared_ptr,比如参数是使用引用传递,所管理的对象不是线程安全的,
* sp1,sp2,sp3实际都是指向A,三个线程同时操作A,对象的数据安全必然需要对象A自己保证。
*/
return 0;
}