本文主要参考:
1)《Effective Mordern C++》
2)《C++ Primer》
3) https://en.cppreference.com/w/cpp/memory/shared_ptr --可在线做实验测试代码
1、shared_ptr的实现原理
智能指针的一种通用实现技术是使用引用计数。智能指针类将一个计数器与智能指针指向的对象相关联,用来记录有多少个智能指针指向相同的对象,并在恰当的时候释放对象。
每次创建类的新对象时,初始化指针并将引用计数置为1;当对象作为另一对象的副本而创建时,引用计数加1;对一个对象进行赋值时,赋值操作符减少左操作数所指对象的引用计数(如果引用计数为减至0,则删除对象),并增加右操作数所指对象的引用计数;调用析构函数时,构造函数减少引用计数(如果引用计数减至0,则删除基础对象)。
下图是shared_ptr的内存模型:
由图中可以看到,实际上引用计数、自定义销毁等都不是直接存储在shared_ptr中,而是通过一个指针指向的一个控制块存储的,控制块是动态分配的内存 。
下面是用类模板简单实现shared_ptr.
#include <iostream>
using namespace std;
template<typename T>
class SmartPointer {
private:
T* p_;
size_t* count_;
public:
SmartPointer(T* ptr = nullptr) : p_(ptr) {
if (p_) {
count_ = new size_t(1);
}
else {
count_ = new size_t(0);
}
}
SmartPointer(const SmartPointer& ptr) {
p_ = ptr.p_;
count_ = ptr.count_;
(*count_)++;
}
SmartPointer& operator=(const SmartPointer& ptr) {
if (p_ == ptr.p_) {
return *this;
}
if (p_) {
if (--(*count_) == 0) {
delete p_;
delete count_;
}
}
p_ = ptr.p_;
count_ = ptr.count_;
(*count_)++;
return *this;
}
~SmartPointer() {
if (--(*count_) == 0) {
delete p_;
delete count_;
}
}
size_t use_count() {
return *count_;
}
};
int main() {
SmartPointer<int> sp1(new int(10));
SmartPointer<int> sp2(sp1);
SmartPointer<int> sp3(new int(20));
sp2 = sp3;
std::cout << sp1.use_count() << std::endl;
std::cout << sp3.use_count() << std::endl;
}
2、智能指针带来的性能影响
1)shared_ptr的尺寸是裸指针的两倍。
2)会带来控制块的开销。
3)引用计数的递增和递减是原子操作,原子操作一般都比非原子操作慢。
3、使用weak_ptr解决shared_ptr的循环引用
解决方案:将类A,B中的shared_prt改为weak_ptr即可,weak_ptr不会增加shared_ptr的引用计数
class B;
class A {
public:
weak_ptr<B> p;
//shared_ptr<B> p;
};
class B {
public:
weak_ptr<A> p;
//shared_ptr<A> p;
};
int main() {
while (true) {
shared_ptr<A> pa(new A());
shared_ptr<B> pb(new B());
pa->p = pb;
pb->p = pa;
}
return 0;
}
4 与shared_ptr关系紧密的几个函数
1)make_shared函数
作用:此函数在动态内存中分配一个对象并初始化它,返回指向此对象的shared_ptr。
用法:make_shared用其参数来构造给定类型的对象,如果不传递任何参数,对象就会进行值初始化。
// 指向一个值为42的shared_ptr
shared_ptr<int> p3 = make_shared<int>(42);
// p4指向一个值为"9999999999"的string
shared_ptr<string> p4 = make_shared<string>(10,'9');
// p5指向一个值初始化的int,即,值为0
shared_ptr<int> p5 = make_shared<int>();
// p6指向一个动态分配的空vector<string>
auto p6 = make_shared<vector<string>>();
2)get函数
作用:向不能使用智能指针的代码传递一个内置指针。
注意:get用来将指针的访问权限传递给代码,你只有在确定代码不会delete指针的情况下,才能使用get。特别是,永远不要用get初始化另一个智能指针或者另一个智能指针赋值。
// shared_ptr::get example
#include <iostream>
#include <memory>
int main () {
int* p = new int (10);
std::shared_ptr<int> a (p);
if (a.get()==p)
std::cout << "a and p point to the same location\n";
// three ways of accessing the same address:
std::cout << *a.get() << "\n";
std::cout << *a << "\n";
std::cout << *p << "\n";
return 0;
}
3)reset函数
作用:重置指针,将一个新的指针赋予一个shared_ptr.
// shared_ptr::reset example
#include <iostream>
#include <memory>
int main () {
std::shared_ptr<int> sp; // empty
sp.reset (new int); // takes ownership of pointer
*sp=10;
std::cout << *sp << '\n';
sp.reset (new int); // deletes managed object, acquires new pointer
*sp=20;
std::cout << *sp << '\n';
sp.reset(); // deletes managed object
return 0;
}
参考博客: