转载自:https://blog.csdn.net/i_chaoren/article/details/82586456
https://www.jianshu.com/p/11389ccbcf79
https://blog.csdn.net/daniel_ustc/article/details/23096229
智能指针的内部实现
- 智能指针最终的实现是 两个指针成员:一个指向数据成员,一个指向计数器成员
- 智能指针里的计数器 维护的是一个指针,指向的 实际内存 在堆上,不是栈上的
智能指针拷贝构造的原理
shared_ptr智能指针内存管理思路
智能指针的一种通用实现技术是使用引用计数。智能指针类将一个计数器与智能指针指向的对象相关联,用来记录有多少个智能指针指向相同的对象,并在恰当的时候释放对象。
每次创建类的新对象时,初始化指针并将引用计数置为1;当对象作为另一对象的副本而创建时,引用计数加1;对一个对象进行赋值时,赋值操作符减少左操作数所指对象的引用计数(如果引用计数为减至0,则删除对象),并增加右操作数所指对象的引用计数;调用析构函数时,构造函数减少引用计数(如果引用计数减至0,则删除基础对象)。
下图是shared_ptr的内存模型:
由图中可以看到,实际上引用计数、自定义销毁等都不是直接存储在shared_ptr中,而是通过一个指针指向的一个控制块存储的,控制块是动态分配的内存(引用计数和指向对象都在堆上、指针在栈上)。
reset
reset的功能是:释放对象,默认情况下置空指针。能够被安全地多次调用。
p.reset()
p.reset(q)
p.reset(q, d)
如果p是唯一指向其对象的shard_ptr, reset会释放对象(unique_ptr则会直接释放)。
如果传递了可选参数内指针q,则会令p指向p;
否则将p置空。
如果还传递了参数d,则将会调用d而不是delete来释放q。释放q的时机不是一定发生在reset时,而是q在运行到需要释放的时候。这里说明的是 reset会传递给它一个单独的delete函数。
智能指针赋值nullptr
表现和reset的行为一致,会递减对象的计数器,如果减为0,则调用析构,释放对象。
class C
{
public:
~C()
{
cout << "C dtor" << endl;
}
};
using namespace cycle_ref;
shared_ptr<cycle_ref::C> sp(new cycle_ref::C());
sp = nullptr;
cout << "before exit" << endl;
运行结果:在赋值nullptr时,调用了析构。
C dtor
before exit
unique_ptr
一个unique_ptr拥有它所指向的对象。
- 与shared_ptr不同,某个时刻只能有一个unique_ptr指向一个给定对象。
- 当unique_ptr被销毁时,它所指向的对象也被销毁。
uniqueptr不支持普通的拷贝和赋值操作,但是可以通过release or reset来转移所有权。
用途
管理一些对象:不需要被共享,但也希望能够在超出作用域时,自动释放资源。
void test_uniq_ptr()
{
std::unique_ptr<int> p1(new int(3));
// std::unique_ptr<int> b;
// b = a; //不支持赋值操作
// std::unique_ptr<int> b = a; //不支持拷贝构造
//把p1指向的对象转移给p2
std::unique_ptr<int> p2(p1.release());// release操作会自动给p1赋空
cout << *p2 << endl;
if (p1 == nullptr)
cout << "p1 is nullptr" << endl;
std::unique_ptr<int> p3(new int(4));
//reset 会释放本对象
//release 会转移所有权
p2.reset(p3.release()); //p2释放原来的对象,同时指向p3所指向的对象,p3赋空
if (p3 == nullptr)
cout << "p3 is nullptr" << endl;
//释放对象,同时赋空
p2.reset();
if (p2 == nullptr)
cout << "p2 is nullptr" << endl;
//release操作会放弃当前指针的控制权,但是并不会销毁它。
//因此通常调用release操作
// 是为了 赋值 给另一个智能指针,
// 而不是 为了销毁对象
std::unique_ptr<int> p(new int(3));
auto px = p.release(); //让出指针所有权,但是并没有销毁
delete px; //这里需要手动delete px
}
weak_ptr
是一种不控制所指向对象生命周期的智能指针,它指向一个shared_ptr管理的对象。
weak_ptr绑定到shared_ptr时,不会改变对象的引用计数。
当shared_ptr被销毁时,指向的对象也被销毁。不论weak_ptr是否指向了它。
用途
在不影响 智能指针所指向对象的生存周期的同时,判断对象是否存在(使用 lock
方法),从而避免 访问一个不存在的对象 的情况。
void test_shared_ptr()
{
std::shared_ptr<int> sp = std::make_shared<int>(3);
cout << *sp << endl;
std::weak_ptr<int> wp(sp);
cout << wp.use_count() << endl;
//expired含义
//判断use_count是否为0? 如果为0,返回true,否则返回false
if (wp.expired())
cout << "obj is deleted" << endl;
else
cout << "obj exist" << endl;
sp.reset();
//lock的含义:
//判断wp指向的对象是否存在?
//如果存在,则返回非空(指向w的对象的智能指针);否则返回空(智能指针)
auto ret = wp.lock();
if (ret)
cout << "obj exist" << endl;
else
cout << "obj not exist" << endl;
sp.reset();
if (sp == nullptr)
cout << "reset will assign nullptr" << endl;
sp.reset();
sp.reset();
cout << "it is safe to call reset any times" << endl;
}
输出结果:
3
1
obj exist
obj not exist
reset will assign nullptr
it is safe to call reset any times
循环引用计数问题
“循环引用”简单来说就是:两个对象互相使用一个shared_ptr成员变量指向对方的会造成循环引用。导致引用计数失效。
#include <iostream>
#include <memory>
using namespace std;
class B;
class A
{
public:// 为了省去一些步骤这里 数据成员也声明为public
//weak_ptr<B> pb;
shared_ptr<B> pb;
void doSomthing()
{
// if(pb.lock())
// {
//
// }
}
~A()
{
cout << "kill A\n";
}
};
class B
{
public:
//weak_ptr<A> pa;
shared_ptr<A> pa;
~B()
{
cout <<"kill B\n";
}
};
int main(int argc, char** argv)
{
shared_ptr<A> sa(new A());
shared_ptr<B> sb(new B());
if(sa && sb)
{
sa->pb=sb;
sb->pa=sa;
}
cout<<"sa use count:"<<sa.use_count()<<endl;
return 0;
}
上面的代码运行结果为:sa use count:2, 注意此时sa,sb都没有释放,产生了内存泄露问题!!!
即A内部有指向B,B内部有指向A,这样对于A,B必定是在A析构后B才析构,对于B,A必定是在B析构后才析构A,这就是循环引用问题,违反常规,导致内存泄露。
一般来讲,解除这种循环引用有下面有三种可行的方法(参考):
1. 当只剩下最后一个引用的时候需要手动打破循环引用释放对象。
2. 当A的生存期超过B的生存期的时候,B改为使用一个普通指针指向A。
3. 使用弱引用的智能指针打破这种循环引用。
虽然这三种方法都可行,但方法1和方法2都需要程序员手动控制,麻烦且容易出错。我们一般使用第三种方法:弱引用的智能指针weak_ptr。
使用weak_ptr来打破循环引用
#include <iostream>
#include <memory>
using namespace std;
class B;
class A
{
public:// 为了省去一些步骤这里 数据成员也声明为public
weak_ptr<B> pb;
//shared_ptr<B> pb;
void doSomthing()
{
shared_ptr<B> pp = pb.lock();
if(pp)//通过lock()方法来判断它所管理的资源是否被释放
{
cout<<"sb use count:"<<pp.use_count()<<endl;
}
}
~A()
{
cout << "kill A\n";
}
};
class B
{
public:
//weak_ptr<A> pa;
shared_ptr<A> pa;
~B()
{
cout <<"kill B\n";
}
};
int main(int argc, char** argv)
{
shared_ptr<A> sa(new A());
shared_ptr<B> sb(new B());
if(sa && sb)
{
sa->pb=sb;
sb->pa=sa;
}
sa->doSomthing();
cout<<"sb use count:"<<sb.use_count()<<endl;
return 0;
}