1. 几种智能指针

1. auto_ptr: c++11中推荐不使用他(放弃)

2. shared_ptr: 拥有共享对象所有权语义的智能指针

3. unique_ptr: 拥有独有对象所有权语义的智能指针

4. weaked_ptr: 到 std::shared_ptr 所管理对象的弱引用

1.1 weak_ptr

参考:https://zh.cppreference.com/w/cpp/memory/weak_ptr

  • std::weak_ptr 是一种智能指针,它对被 std::shared_ptr 管理的对象存在 非拥有性(“弱”)引用。在访问所引用的对象前必须 先转换为 std::shared_ptr

  • std::weak_ptr 用来 表达临时所有权的概念

    • 当某个对象只有存在时才需要被访问,而且随时可能被他人删除时,可以使用 std::weak_ptr 来跟踪该对象。

    • 需要获得临时所有权时,则将其转换为 std::shared_ptr,此时如果原来的 std::shared_ptr被销毁,则该对象的生命期将被延长至这个临时的 std::shared_ptr 同样被销毁为止。

  • std::weak_ptr 的另一用法是:打断 std::shared_ptr 所管理的对象组成的环状引用。(打破shared_ptr的循环引用)

    • 若这种环被孤立(例如无指向环中的外部共享指针),则 shared_ptr 引用计数无法抵达零,而内存被泄露。

    • 能令环中的指针之一为弱指针以避免此情况。

循环引用的问题:该被调用的析构函数没有被调用

#include <iostream>
#include <memory>
using namespace std; class Parent;
typedef std::shared_ptr<Parent> ParentPtr; class Child
{
public:
ParentPtr father;
Child() {
cout << "hello Child" << endl;
}
~Child() {
cout << "bye Child\n";
}
}; typedef std::shared_ptr<Child> ChildPtr; class Parent {
public:
ChildPtr son;
Parent() {
cout << "hello parent\n";
}
~Parent() {
cout << "bye Parent\n";
}
}; void testParentAndChild()
{
ParentPtr p(new Parent());
ChildPtr c(new Child());
p->son = c;
c->father = p;
} int main()
{
testParentAndChild();
return ;
}

问题:c只有调用p的析构的时候,才能被释放。p只有调用c的析构的时候,才能被释放。。形成了循环引用,造成了内存泄露

因此,引入新的智能指针,weak_ptr

1.2 weak_ptr基本用法

一旦外部指向shared_ptr资源失效,那么weak_ptr管理的资源自动失效

#include <iostream>
#include <cassert>
#include <memory>
using namespace std; class Object
{
public:
Object(int id) : m_id(id) {
std::cout << "init obj " << m_id << std::endl;
}
~Object() {
std::cout << "bye bye " << m_id << std::endl;
}
int id() const {
return m_id;
}
private:
int m_id;
}; void sharedPtrWithWeakPtr()
{
typedef std::shared_ptr<Object> ObjectPtr;
typedef weak_ptr<Object> WeakObjectPtr; ObjectPtr obj(new Object()); // 一旦外部指向shared_ptr资源失效,那么weak_ptr管理的资源自动失效 WeakObjectPtr weakObj2; // 裸指针
WeakObjectPtr weakObj(obj); // 指向shared_ptr指针
WeakObjectPtr weakObj3(obj);
cout << "obj use count is " << obj.use_count() << endl; // 1--尽管weak_ptr也指向obj,但他只是监听者,本身并不影响引用计数次数
{
// weak_ptr使用方法
// 外部至少还有一个shared_ptr来管理资源,同时p自己本身的资源 -- p.use_count >= 2
auto p = weakObj.lock(); // auto == ObjectPtr
if (p) {
cout << p.unique() << endl; // 0 -- p.use_count() >= 2
}
else {
}
}
// 不指向某份资源
// obj.reset();
// {
// auto p = weakObj.lock(); // auto == ObjectPtr
// if (p) {
// assert(false);
// }
// else {
// cout << "null" << endl; // 如果调用reset,就会执行这句话
// }
// } // 此时, Object(1)已经失效
obj.reset(new Object()) ;
{
auto p = weakObj.lock(); // 返回值如果有效, 在外部必须有weakObj指向同一个资源
if (p) {
assert(false);
}
else {
cout << "null " << endl; // null
}
} weakObj = obj; // 又有效了
// 想知道weak_ptr有没有管理一份资源(外部有没有资源), 又不想生成一个shared_ptr
if (weakObj.expired()) {
// 资源过期了 -- true
cout << "no ptr" << endl;
}
else {
cout << "have resource\n";
}
} int main()
{ sharedPtrWithWeakPtr();
return ; }

1.3 解决类之间循环引用

一旦外部指向shared_ptr资源失效,那么weak_ptr管理的资源自动失效

#include <iostream>
#include <cassert>
#include <memory>
using namespace std; class Parent;
typedef std::shared_ptr<Parent> ParentPtr;
typedef std::weak_ptr<Parent> WeakParentPtr; class Child
{
public:
WeakParentPtr father; // 只有一环换成 weak_ptr, 即可打破环
Child() {
cout << "hello Child" << endl;
}
~Child() {
cout << "bye Child\n";
}
}; typedef std::shared_ptr<Child> ChildPtr;
typedef std::weak_ptr<Child> WeakChildPtr; class Parent {
public:
ChildPtr son;
Parent() {
cout << "hello parent\n";
}
~Parent() {
cout << "bye Parent\n";
}
}; void testParentAndChild()
{
ParentPtr p(new Parent());
ChildPtr c(new Child());
p->son = c;
c->father = p;
cout << (c->father).use_count() << endl;
cout << (p->son).use_count() << endl;
} int main()
{
testParentAndChild();
return ;
}

析构函数成功调用, 注意析构顺序,谁是weak_ptr对象,就先析构谁。

1.4 用 enable_shared_from_this从this转换到shared_ptr

  • 类中函数接口需要一个本对象智能指针的const引用 (如何生成本对象的智能指针)

  • 用 enable_shared_from_this从this转换到shared_ptr  (使用CRTP来使用 本对象的智能指针)

主要代码:

handleChildAndParent(shared_from_this(), ps);

完整代码:

#include <iostream>
#include <cassert>
#include <memory>
using namespace std; class Parent;
typedef std::shared_ptr<Parent> ParentPtr;
typedef std::weak_ptr<Parent> WeakParentPtr; class Child : public std::enable_shared_from_this<Child> // 奇异模板参数 CRTP
{
public:
WeakParentPtr father; // 只有一环换成 weak_ptr, 即可打破环
Child() {
cout << "hello Child" << endl;
}
~Child() {
cout << "bye Child\n";
}
void CheckRelation() { }
}; typedef std::shared_ptr<Child> ChildPtr;
typedef std::weak_ptr<Child> WeakChildPtr; void handleChildAndParent(const ParentPtr& p, const ChildPtr& c); class Parent : public enable_shared_from_this<Parent> {
public:
WeakChildPtr son;
Parent() {
cout << "hello parent\n";
}
~Parent() {
cout << "bye Parent\n";
}
void CheckRelation() {
auto ps = son.lock();
if (ps) {
//原理: 类中存在weak_ptr指针,通过一系列方式转换成 shared_ptr,然后传参
handleChildAndParent(shared_from_this(), ps); // 使用CRTP来使用 本对象的指针 ★ ★ ★ ★
}
cout << "after call checkRelation\n";
}
}; void testParentAndChild()
{
ParentPtr p(new Parent());
ChildPtr c(new Child());
p->son = c;
c->father = p;
cout << (c->father).use_count() << endl;
cout << (p->son).use_count() << endl; p->CheckRelation();
} void handleChildAndParentRef(const Parent& p, const Child& c)
{
auto cp = c.father.lock();
auto pc = p.son.lock();
if (cp.get() == &p && pc.get() == &c)
{
cout << "right relation" << endl;
}
else {
cout << "oop !!!\n";
}
} // const xxx&: 减少拷贝次数
void handleChildAndParent(const ParentPtr& p, const ChildPtr& c)
{
auto cp = c->father.lock();
auto pc = p->son.lock();
if (cp == p && pc == c)
{
cout << "right relation\n";
}
else {
cout << "oop!!!\n";
}
} int main()
{
testParentAndChild();
return ;
}

 

深入学习c++--智能指针(二) weak_ptr(打破shared_ptr循环引用)的更多相关文章

  1. 【STL学习】智能指针之weak_ptr

    简介 weak_ptr是shared_ptr的观察者,它不会干扰shared_ptr所共享对象的所有权,当一个weak_ptr所观察的shared_ptr要释放它的资源时,它会把相关的weak_ptr ...

  2. [6] 智能指针boost::weak_ptr

    [1]boost::weak_ptr简介 boost::weak_ptr属于boost库,定义在namespace boost中,包含头文件 #include<boost/weak_ptr.hp ...

  3. 【C++11新特性】 C++11智能指针之weak_ptr

    如题,我们今天要讲的是C++11引入的三种智能指针中的最后一个:weak_ptr.在学习weak_ptr之前最好对shared_ptr有所了解.如果你还不知道shared_ptr是何物,可以看看我的另 ...

  4. c++智能指针(unique_ptr 、shared_ptr、weak_ptr、auto_ptr)

    一.前序 什么是智能指针? ——是一个类,用来存储指针(指向动态分配对象也就是堆中对象的的指针). c++的内存管理是让很多人头疼的事,当我们写一个new语句时,一般就会立即把delete语句直接也写 ...

  5. 【校招面试 之 C/C++】第26题 C++ 智能指针(二)之 share_ptr

    1.综述 shared_ptr 是一个标准的共享所有权的智能指针, 允许多个指针指向同一个对象. 定义在 memory 文件中(非memory.h), 命名空间为 std. shared_ptr 是为 ...

  6. 智能指针之 weak_ptr

    1. weak_ptr 介绍 std::weak_ptr 是一种智能指针,它对被 std::shared_ptr 管理的对象存在非拥有性("弱")引用.在访问所引用的对象指针前必须 ...

  7. C++2.0新特性(七)——<Smart Pointer(智能指针)之weak_ptr>

    一.weak_ptr出现的意义 上一节提到过shared_ptr,它会自动释放“不再需要使用的对象”的相应的资源,但是它不是万能的,在某些时候(比如说循环引用),它会显得力不从心,这就是weak_pt ...

  8. c++智能指针的使用,shared_ptr,unique_ptr,weak_ptr

    c++智能指针的使用 官方参考 普通指针的烦恼:内存泄漏,多次释放,提前释放 智能指针 负责自动释放所指向的对象. 三种智能指针 shared_ptr,unique_ptr,weak_ptr: 将sh ...

  9. 深入学习c++--智能指针(一) shared_ptr

    1. 几种智能指针 1. auto_ptr: c++11中推荐不使用他 2. shared_ptr: 每添加一次引用 就+1,减少一次引用,就-1:做到指针进行共享 3. unique_ptr: 一个 ...

随机推荐

  1. 「NOI2012」骑行川藏

    「NOI2012」骑行川藏 题目描述 蛋蛋非常热衷于挑战自我,今年暑假他准备沿川藏线骑着自行车从成都前往拉萨. 川藏线的沿途有着非常美丽的风景,但在这一路上也有着很多的艰难险阻,路况变化多端,而蛋蛋的 ...

  2. EPL II 编程打印

    一.EPL II 格式及打印测试 注意N命令前的换行和最后P1后的换行.将此段代码复制到windows记事本里另存为Print.ext,文件名随便,后缀为ext.然后通过cmd控制命令行输入" ...

  3. python实现抽样分布描述

    本次使用木东居士提供数据案例,验证数据分布等内容, 参考链接:https://www.jianshu.com/p/6522cd0f4278 #数据读取 df = pd.read_excel('C:// ...

  4. Python之sort()函数详解

    #从小到大排列 print(sorted([36, 5, -12, 9, -21])) #将待排序的值放入到key中的函数中,在进行比较排序 print(sorted([36, 5, -12, 9, ...

  5. LOJ P10065 北极通讯网络 题解

    每日一题 day39 打卡 Analysis 1.当正向思考受阻时,逆向思维可能有奇效. 2.问题转化为:找到最小的d,使去掉所有权值>d的边之后,连通支的个数<k; 3.定理:如果去掉所 ...

  6. 搭建使用PHPstorm环境

    本周学习内容: 1.学习PHP: 2.复习技能表: 3.学习正则表达式: 实验内容: 1.安装PHPstorm环境,破解PHPstorm: 2.PHPstorm运行PHP代码 3.PHPstorm安装 ...

  7. Ubuntu16.4 内核降级

    .cp /etc/apt/sources.list /etc/apt/sources.list.bak #备份sources.list .vi /etc/apt/sources.list #在sour ...

  8. Python 全栈开发【第0篇】:目录

    Python 全栈开发[第0篇]:目录   第一阶段:Python 开发入门 Python 全栈开发[第一篇]:计算机原理&Linux系统入门 Python 全栈开发[第二篇]:Python基 ...

  9. 微信小程序 长按文字复制与按钮复制

    1. 长按文字复制 当要实现长按文字进行复制的时候,需要使用text标签,并将selectable属性设置为true <text class='url-txt' selectable='true ...

  10. 在Ubuntu 18.04 下安装mysql,没有初始密码,重设root密码

    在Ubuntu 18.04 下安装mysql 不知道是由于mysql更新为新版还是.Ubuntu18.04中的特性,安装过程中没有设置密码的环节,在网络上找了半天,总算解决了!特此记录下来,以便以后查 ...