目录
我们为什么需要smart pointersmart pointer基本概念之引用计数smart pointer之shared_ptr自定义deleter(也就是自定义删除器)shared_ptr之make_shared智能指针存在的问题之循环引用解决循环引用之weak_ptr智能指针问题解决方法如下我们为什么需要smart>
众所周知 新手写的c++代码是很恐怖 压根就不能用 其中最大的原因就在于新手写的代码可能存在大量的内存泄漏 那么为什么新手无法很好的去掌握内存的东西呢 就是因为原生的c++并不像java那样存在垃圾回收的机制 申请在堆区的资源都需要自己去回收 然而最痛苦的一件事情在于 指针的生命周期结束时 你会不小心就没去回收他在堆区的资源 因为堆区资源的生命周期是很难把握的 有可能你析构了 直接导致野指针访问异常那么为了解决这个问题 c++就推出了智能指针 其中最重要的三种指针就是shared_ptr unique_ptr weak_ptr 接下来让我们来讲讲如何将智能指针的生命周期和堆区资源的生命周期绑定起来吧
其实也非常简单 本质就是当这片堆区资源的引用计数变为0的时候就释放这片内存
smart>
先来说说引用计数 这个东西是stl保证了肯定是线程安全的 所以即使你在多个线程内同时去增加或者同时减少引用计数也并不会让引用计数的值出现非你预期的结果
智能指针是和引用计数绑定在一起的 当你创建智能指针指向一片资源时 引用计数就加一 当智能指针析构时 引用计数就减一 当引用计数变为0时 堆区资源被析构
smart>
让我们来看看下一段代码
int main()
{
std::shared_ptr<std::string> i(new std::string("its good"));
std::shared_ptr<std::string> j(new std::string("its bad"));
std::vector<std::shared_ptr<std::string>> smartPointer_vec;
for(int k=0;k<5;k++)
smartPointer_vec.emplace_back(i);
for (int k = 0; k < 4; k++)
smartPointer_vec.emplace_back(j);
for (auto &i : smartPointer_vec)
{
std::cout<<i->c_str();
std::cout << i.use_count() << " ";
std::cout << j.use_count() << std::endl;
i = nullptr;
}
std::cout << i->c_str();
std::cout << i.use_count() <<" ";
std::cout << j.use_count() << std::endl;
}
聪明人看输出 你就能完全明白 当引用计数为0的时候就会析构 其他不多说了

重要讲解:首先使用share_ptr去指向new出来的数据是性能低效的 最本质的原因在于 他会进行两次内存分配 第一次是对象堆区资源的申请 然后才是引用计数堆区资源的申请 而使用make_shared可以只进行一次内存分配 所以他更快 并且更安全 并且c++标准委员会也推荐你这么做 关于make_shared等下讲解
自定义deleter(也就是自定义删除器)
先说我们为什么需要自定义删除器>

正确的写法如下

即自定义一个删除器 当然你也可以玩一些移动操作 也就是花哨的操作 当然花哨操作就很多了 我只演示其中一种如下图所示

运行结果截图如下:

Tips:当你非常清楚你在干什么的时候再玩 功力不够 不要乱玩
shared_ptr之make_shared
上文我们说过>
int main()
{
std::shared_ptr<int>p1(new int(5));
//下面这种方式比上面这种方式性能更快 并且更加安全
std::shared_ptr<int>p2 = make_shared<int>(5);
}
当你使用make_shared的时候 又想去使用智能指针指向一个数组的时候 一个推荐的做法如下
int main()
{
std::shared_ptr<std::vector<int>>p1(new std::vector<int>());
//下面这种方式比上面这种方式性能更快 并且更加安全
std::shared_ptr<std::vector<int>>p2 = make_shared<std::vector<int>>();
}
智能指针存在的问题之循环引用
那么现在我们来看看shared_ptr存在的一些问题>
class SmartPointerTest
{
public:
std::shared_ptr<SmartPointerTest> LoopRef{};
int p[1000]{};
};
int main()
{
std::shared_ptr<SmartPointerTest>p1(new SmartPointerTest());
std::shared_ptr<SmartPointerTest>p2(new SmartPointerTest());
p1->LoopRef = p2;
p2->LoopRef = p1;
}
可以明显看到 我们创建了两个智能指针p1和p2 而p1指向的堆区资源里又有智能指针指向p2的堆区资源 同理p2 而当main函数结束的时候 p1 p2指针被释放 但是 这个时候 因为两片堆区资源的引用计数都没被置为0 所以不会释放 那么这片堆区内存也就永远的泄漏了 这是所有循环引用的原型 无论任何再复杂的循环引用都是建立在这个最基本的循环引用之上的
解决循环引用之weak_ptr
我们现在希望有一个方法来解决循环引用的问题>
class SmartPointerTest
{
public:
std::weak_ptr<SmartPointerTest> LoopRef{};
int p[1000]{};
};
int main()
{
std::shared_ptr<SmartPointerTest>p1(new SmartPointerTest());
std::shared_ptr<SmartPointerTest>p2(new SmartPointerTest());
p1->LoopRef = p2;
p2->LoopRef = p1;
//当你想使用资源的时候 用下面的操作进行
std::cout << p1->LoopRef.lock()->p << std::endl;
}
输出结果如下:

Tips:当然weak_ptr的作用远远不止如此 他存在的意义仅仅是你想共享资源但是你并不想增加引用计数 解决循环引用只是顺便解决的优秀的程序员总是能知道在什么情况下使用何种指针来达到性能最优 lock()函数 顾名思义是要去给引用计数上锁的 频繁上锁带来的性能问题不用多说了吧
如果weak_ptr指向的资源已经被析构 那么他会抛出bad_weak_ptr的异常 请注意捕获异常
智能指针问题
无法创建指向自己的智能指针(本质当创建自己的智能指针时会创建两个所属组)
什么叫无法创建指向自己的智能指针呢>
class SmartPointerTest
{
public:
std::weak_ptr<SmartPointerTest> LoopRef{};
int p[1000]{};
std::vector<std::shared_ptr<SmartPointerTest>> spt_vec;
void MemberFuncTest()
{
spt_vec.push_back(std::shared_ptr<SmartPointerTest>(this));
}
int operator[](int i)
{
return p[i];
}
};
int main()
{
std::shared_ptr<SmartPointerTest>p1(new SmartPointerTest());
p1->MemberFuncTest();
std::cout<<p1.use_count()<<std::endl;
system("pause");
}
我们预期的结果是把指向自己的智能指针传入 并且引用计数为2 但是运行结果如下:

并且程序会崩溃 为什么呢 因为你重复释放了 这就是我说的 你会创建两个组 而不是单纯的增加引用计数 其本质还是滥用普通指针和智能指针引起的麻烦
解决方法如下
代码如下>
class SmartPointerTest :std::enable_shared_from_this<SmartPointerTest>
{
public:
std::weak_ptr<SmartPointerTest> LoopRef{};
int p[1000]{};
std::vector<std::shared_ptr<SmartPointerTest>> spt_vec;
void MemberFuncTest()
{
spt_vec.push_back(std::shared_ptr<SmartPointerTest>(shared_from_this()));
}
int operator[](int i)
{
return p[i];
}
};
int main()
{
std::shared_ptr<SmartPointerTest>p1(new SmartPointerTest());
p1->MemberFuncTest();
std::cout<<p1.use_count()<<std::endl;
system("pause");
}
当你这样继承自enable_shared_from_this的时候你就可以将自身的智能指针传入而不是创建一个新的组避免了重复释放非常的方便
关于unique_ptr我们将会在下一篇文章进行详细讲解其实也很简单就是他堆区资源的引用计数永远只可能是一也就是说他的资源只可能被一个指针指向附带而来的有一些小细节和普通的shared_ptr不同我们也就留在下一章再说了
到此这篇关于C++ smart pointer全面深入讲解的文章就介绍到这了,更多相关C++ smart pointer内容请搜索易采站长站以前的文章或继续浏览下面的相关文章希望大家以后多多支持易采站长站!










