首先智能指针的出现是为了应对多个指针指向同一块内存的情况,我们这里讲的只能指针分为2种分别是std::shared_ptr
,std::unique_ptr
对于std::shared_ptr
当我们多个智能指针指向一块内存的时候,他们可以同时操作这块区域,和普通指针没什么两样,但是当我们的所有智能指针(指向同一块内存)超出生命周期(超过范围scope)后,那么这块内存会自动的被销毁,防止出现内存泄漏
对于std::unique_ptr
就完全不一样,一块内存区域只能被一个std::shared_ptr
指,这个std::shared_ptr
指针不能copy给别的对于std::shared_ptr
变量,只能使用std::move()
在使用std::move()
的同时,原std::unique_ptr
对于原来指向的内存区域的所有权被release了,当std::unique_ptr
超出作用域,那么其指向的内存区域就自动销毁
#include
#include
#include
class A{
public:
A(){ std::cout << "construct!!!" << std::endl; };
~A(){ std::cout << "destruct!!!" << std::endl; };
};
void func(std::shared_ptr<A> impl){
std::shared_ptr<A> impl2 = impl;
}
int main(){
auto a = new A;
std::shared_ptr<A> impl1_(a);
func(impl1_);
std::cout << "back to main" << std::endl;
//std::shared_ptr impl2_(a);
}
在上述的简单代码中,main函数第二行代表我们用一个std::shared_ptr
指向一块内存区域,再在函数func中我们用另一个std::shared_ptr
指向同样的区域,在func返回(std::shared_ptr
)被销毁后,并没有触发析构函数,而是在std::shared_ptr
超出作用域的时候自动触发析构,
注意使用=号给shared_ptr
赋值的时候只能传递shared_ptr
对象,而非被shared_ptr
指向的对象如下
auto a = new A;
std::shared_ptr<A> sp = a; //error!!!智能指针用等号赋值的时候智能传递智能指针对象,而不能传递智能指针指向的对象
std::shared_ptr<A> sp(a); //yes!!!如果想传递被智能指针管理的对象,可以直接调用智能指针sp的构造函数
还有一个问题是我们如果先new一个对象,将这个对象赋值给shared_ptr
,在shared_ptr
对象生命周期结束前先delete刚刚new的对象,最后等shared_ptr
生命周期结束后会发生double delete
多个shared_ptr
之间共享多个数据包含2个reference counter和其他数据
下图描述的很好
2个counter的作用不一样,第一个对于,当counter for the std::shared_ptr为0就会调用析构函数清除管理的obj,当counter for the std::weak_ptr.为0就会析构shared_pointer本身
看下面代码为何会报错
#include
#include
#include
class A{
int data;
public:
A(){}
A(int i ):data(i){}
A(const A&& rhs){ std::cout << "move construct" << std::endl;}
A(const A& rhs) { std::cout << "copy construct " << std::endl; }
};
-int main(){
std::vector< std::shared_ptr<A> > v;
v.push_back(new A(1));
return 0;
}
因为push_back()
的时候是=号赋值,而shared_ptr
在使用=号赋值的时候只接收shared_ptr
对象,不能接收被shared_ptr
管理的对象,所以需要先将push_back()
中push的对象换成shared_ptr
,如何换成shared_ptr
,使用make_shared<>()
#include
#include
#include
class A{
int data;
public:
A(){std::cout << "common construct" << std::endl;}
A(int i ):data(i){}
A(const A&& rhs){ std::cout << "move construct" << std::endl;}
A(const A& rhs) { std::cout << "copy construct " << std::endl; }
};
int main(){
std::vector< std::shared_ptr<A> > v;
v.push_back(std::make_shared<A>());
}
先看下面的代码
#include
#include
struct B;
struct A {
std::shared_ptr<B> b;
~A() { std::cout << "~A()\n"; }
};
struct B {
std::shared_ptr<A> a;
~B() { std::cout << "~B()\n"; }
};
void useAnB() {
auto a = std::make_shared<A>();
auto b = std::make_shared<B>();
a->b = b;
b->a = a;
}
int main() {
useAnB();
std::cout << "Finished using A and B\n";
}
上面的程序除了程序结束,std::make_shared
和std::make_shared
所创建的A obj和B obj永远不会被释放,因为上述的程序是一个经典的std::shared_ptr cyclic references
--- useAnB
A obj <----------------------------> B obj
↑ ↑
| |
| |
| |
| |
shared_ptr a shared_ptr b
只想完函数useAnB后shared_ptr a中记录的对象A obj被引用了2次(一次是shared_ptr a还有一次是B obj),B obj同样他的reference count也是2,所以在程序结束前对象A obj和对象B obj之间相互引用的结不解开他们就不能被析构,遗憾的是我们用的std::make_shared
,不能直接访问对象本身…
所以这里有个概念被提出来叫做std::weak_prt
,也就是说weak指针没有关于他所指向对象的主权,不能通过weak_ptr去调用他所指向的对象,换句话说weak_ptr和shared_ptr一起使用的时候weak_ptr不会增加shared_ptr的reference count,还是上面代码我们用weak_ptr
#include
#include
struct B;
struct A {
std::weak_ptr<B> b;
~A() { std::cout << "~A()\n"; }
};
struct B {
std::weak_ptr<A> a;
~B() { std::cout << "~B()\n"; }
};
void useAnB() {
auto a = std::make_shared<A>();
auto b = std::make_shared<B>();
a->b = b;
b->a = a;
}
int main() {
useAnB();
std::cout << "Finished using A and B\n";
}
在执行完useAnB后对象B和对象A的reference count都是1,都是由函数useAnB中make_shared所指向的
#include
#include
class A{
public:
A(){ std::cout << "construct!!!" << std::endl; };
~A(){ std::cout << "destruct!!!" << std::endl; };
};
void func(std::shared_ptr<A> impl){
std::shared_ptr<A> impl2 = impl;
}
int main(){
std::unique_ptr<A> p1(new A);
//std::unique_ptr p2 = p1; //error 因为一块内存区域new A只能被一个std::unique_ptr指针所指
std::unique_ptr<A> p3 = std::move(p1);
}
我们直接看main函数第二行,假如release注释就会报错,因为一块内存区域new A只能被一个std::unique_ptr
指针所指
main函数第三行右边的std::move()
代表std::unique_ptr
p1对原有的内存区域的所有权被release,并且所有权转移给unique_ptr
p3,换句话说此时的new A的这一块内存区域被p3这个智能指针所指向,而非之前的p1
我一般在class中使用 std::shared_ptr
去指向,private
中的所有变量,且private
中的所有变量都存放在一个struct
中,代码如下
#include
#include
#include
class A{
public:
A();
~A();
private:
struct impl;
std::shared_ptr<struct impl> impl_;
};
struct A::impl{
int id;
std::string name;
};
A::A()
: impl_(new struct A::impl){
}
当我们的class对象作用域消失,那么对应的智能指针std::shared_ptr
也会随着消失,当智能指针小时其指向的内存区域也就是struct的内容也会消失,避免了我们忘记release class中的某些成员,假如这些成员占用内存很大,且是放于heap中,并且忘记在析构函数中remalloc或者delete,那么就会内存泄漏,如果用std::shared_ptr
将其"包起来",我们就可以避免这些烦恼