c++ 学习系列 -- 智能指针

一   为什么引入智能指针?解决了什么问题?

C++ 程序设计中使用堆内存是非常频繁的操作,堆内存的申请和释放都由程序员自己管理。但使用普通指针,容易造成内存泄露(忘记释放)、二次释放、程序发生异常时内存泄露等问题等。

另外,使用普通指针容易产生 野指针、悬空指针 等问题。

所以 C++11 就引入了智能指针来管理内存。

二  常用的智能指针与区别

常用智能指针有  shared_ptrunique_ptr weak_ptr 

  • unique_ptr: 独占式指针,同一时刻只允许有一个 unique_ptr 指针指向一个对象
  • shared_ptr: 共享式指针,同一时刻允许多个 shared_ptr 指针指向同一个对象。
    •  缺点:出现相互引用时,容易导致死锁或者内存无法释放内存的问题
  • weak_ptr: 为了解决 shared_ptr 相互引用可能导致的死锁或无法释放内存的问题而引入,通常与shared_ptr 配合使用。但是由于缺少引用计数,一旦最后一个指向对象的shared_ptr被销毁,对象就会被释放。即使有weak_ptr指向对象,对象也还是会被释放。 C++11智能指针(weak_ptr) - 简书 (jianshu.com)

三  常用智能指针使用例子

   1  unique_ptr 例子

        unique_ptr 是一个独享所有权的智能指针

#include<memory>
#include<string>
#include<iostream>class Person
{
public:Person(std::string name):m_name(name){std::cout << "Person constructor name: " << m_name << std::endl;}~Person(){std::cout << "Person destructor name: " << m_name << std::endl;}private:std::string m_name;
};#include<memory>void testUniquePtr()
{std::unique_ptr<Person>  p1_ptr(new Person("P1 ---"));std::unique_ptr<Person>  p2_ptr = std::move(p1_ptr);// std::unique_ptr<Person>  p3_ptr = p2_ptr; // 编译不过// std::unique_ptr<Person>  p4_ptr(p2_ptr); // 编译不过}int main(int argc, char *argv[])
{testUniquePtr();return 0;
}

   输出

2   shared_ptr 例子

#include<memory>
#include<iostream>
#include<string>using namespace std;void testSharedPtr()
{shared_ptr<string> pa(new string("PAAAA"));shared_ptr<string> pb(new string("PBBBB"));cout << "*pa " << *pa << endl;//CHNcout << "pa.use_count " << pa.use_count() << endl;//1cout << "*pb " << *pb << endl;//USAcout << "pb.use_count " << pb.use_count() << endl;//1pa = pb;cout << *pa << endl;//USAcout << "pa.use_count " << pa.use_count() << endl;//2:pa和pb指向同一个资源USA了,该资源的计数为2,所以pb、pb都输出2cout << "pb.use_count " << pb.use_count() << endl;//2pa.reset();pb.reset();cout << "pa.use_count " << pa.use_count() << endl;//0cout << "pb.use_count " << pb.use_count() << endl;//0
}int main(int argc, char *argv[])
{testSharedPtr();return 0;
}

   3  weak_ptr 例子

       3.1  shared_ptr 相互引用的问题

// a.h
#include<memory>class B;class A
{
public:A(){std::cout << "A constructor ---" << std::endl;}~A(){std::cout << "A destructor ---" << std::endl;}public:std::shared_ptr<B> m_b_ptr;
};// b.h
#include<memory>class A;class B
{
public:B(){std::cout << "A constructor ---" << std::endl;}~B(){std::cout << "A destructor ---" << std::endl;}public:std::shared_ptr<A> m_a_ptr;
};// main.cpp
void testSharedPtr()
{std::shared_ptr<A> pa(new A);cout << "pa.use_count " << pa.use_count() << endl;//1std::shared_ptr<B> pb(new B);cout << "pb.use_count " << pb.use_count() << endl;//1pa->m_b_ptr = pb;cout << "pb.use_count " << pb.use_count() << endl;//2cout << "pa.use_count " << pa.use_count() << endl;//1pb->m_a_ptr = pa;//由于share_ptr是共享资源,所以pb所指向的资源的引用计数也会加1cout << "pb.use_count " << pb.use_count() << endl;//2cout << "pa.use_count " << pa.use_count() << endl;//2}int main(int argc, char *argv[])
{testSharedPtr();return 0;
}

  输出 :     

通过输出可以看到未执行析构函数,存在内存泄漏。

引用计数分别增加到了 2 ,不为 0 就意味着无法释放内存。

3.2  weak_ptr 与 share_ptr 使用 

// a.h
#include<memory>class B;class A
{
public:A(){std::cout << "A constructor ---" << std::endl;}~A(){std::cout << "A destructor ---" << std::endl;}public:std::weak_ptr<B> m_b_ptr;
};// b.h
#include<memory>class A;class B
{
public:B(){std::cout << "A constructor ---" << std::endl;}~B(){std::cout << "A destructor ---" << std::endl;}public:std::shared_ptr<A> m_a_ptr;
};// main.cpp
void testWeakPtr()
{std::shared_ptr<A> pa(new A);cout << "pa.use_count " << pa.use_count() << endl;//1std::shared_ptr<B> pb(new B);cout << "pb.use_count " << pb.use_count() << endl;//1pa->m_b_ptr = pb;cout << "pb.use_count " << pb.use_count() << endl;//1cout << "pa.use_count " << pa.use_count() << endl;//2pb->m_a_ptr = pa;cout << "pb.use_count " << pb.use_count() << endl;//1  由于 weak_ptr 是弱引用,不会增加引用计数cout << "pa.use_count " << pa.use_count() << endl;//2  由于share_ptr是共享资源,所以pb所指向的资源的引用计数也会加1}int main(int argc, char *argv[])
{testWeakPtr();return 0;
}

输出:

 通过输出可以看到执行析构函数,不存在内存泄漏。

//  资源B的引用计数一直就只有1,当pb析构时,B的计数减一,变为0,B得到释放,
//  B释放的同时也会使A的计数减一,同时pa自己析构时也会使资源A的计数减一,那么A的计数为0,A得到释放。

四  智能指针的原理与简单实现

智能指针实际运用的就是c++ 中的 RAII 技术,详情见  C++ 学习系列 二 -- RAII 机制_在河之洲木水的博客-CSDN博客

1. unique_ptr 

 因为是独占型指针,不可以拷贝与赋值,所以需要禁止拷贝构造函数与赋值函数

// my_nuique_ptr.h
template<typename T>
class my_unique_ptr
{
public:my_unique_ptr(T* ptr = nullptr);~my_unique_ptr();my_unique_ptr(my_unique_ptr&& other_ptr); // c++ 中声明移动构造函数后,则自动禁用拷贝构造函数my_unique_ptr& operator=(my_unique_ptr&& other_ptr); // c++ 中声明移动赋值函数后,则自动禁用拷贝赋值函数T& operator*() const; // 指针的基本操作,取值T* operator->() const;operator bool() const; // 提供一个本类型到bool的隐式转换,不允许使用参数private:T* m_ptr;
};template<typename T>
my_unique_ptr<T>::my_unique_ptr(T* ptr):m_ptr(ptr)
{}template<typename T>
my_unique_ptr<T>::~my_unique_ptr()
{delete m_ptr;
}template<typename T>
my_unique_ptr<T>::my_unique_ptr(my_unique_ptr&& other_ptr)
{this->m_ptr = other_ptr.m_ptr;other_ptr.m_ptr = nullptr;
}template<typename T>
my_unique_ptr<T>&
my_unique_ptr<T>::operator=(my_unique_ptr&& other_ptr)
{this->m_ptr = other_ptr.m_ptr;other_ptr.m_ptr = nullptr;return this;
}template<typename T>
T&   my_unique_ptr<T>::operator*() const
{return *m_ptr;
}template<typename T>
T* my_unique_ptr<T>::operator->() const
{return m_ptr;
}template<typename T>
my_unique_ptr<T>::operator bool() const
{return m_ptr;
}// main.cpp#include<iostream>
#include"my_unique_ptr.h"void testFunc2()
{my_unique_ptr<Person> my_ptr(new Person("p1 ------"));std::cout << "person name1: "<<my_ptr->getName() << std::endl;my_unique_ptr<Person> my_ptr2(std::move(my_ptr));std::cout << "person name2: "<<my_ptr2->getName() << std::endl;//my_unique_ptr<Person> my_ptr3 = my_ptr; // 编译失败// my_unique_ptr<Person> my_ptr4(my_ptr); // 编译失败}int main()
{testFunc2();return 0;
}

输出:

2. shared_ptr

shared_ptr 是共享型指针,同一时刻可以右多个指针指向同一个对象,只有最后一个指针离开作用域时,才会调用对象的析构函数,释放对象中的资源。

那么是如何实现的呢?

答案是:利用引用计数法。在 类 shared_ptr 中定义一个成员变量引用计数 share_count ,当有一个指针指向相同的对象时,就将 share_count 就自增 1,为了各 shared_ptr 的引用计数 share_count 同时增加,可以将 share_count 手动开辟一个空间,用普通指针指向它。

若是考虑到多线程的场景,还应该将 引用计数 share_count 加上锁才可以。

// my_shared_ptr.h
#include <mutex>
static std::mutex gMutex;template<typename T>
class my_shared_ptr
{
public:my_shared_ptr(T* ptr = nullptr);~my_shared_ptr();my_shared_ptr(my_shared_ptr& other_ptr);my_shared_ptr& operator=(my_shared_ptr& other_ptr);T& operator*();T* operator->();int user_count();private:void addCount();void minusCount();T* m_ptr;int* share_count = nullptr;
};template<typename T>
my_shared_ptr<T>::my_shared_ptr(T* ptr):m_ptr(ptr)
{if(!share_count){share_count = new int(1);}
}template<typename T>
my_shared_ptr<T>::~my_shared_ptr()
{minusCount();if((*this->share_count) == 0 && m_ptr)delete m_ptr;
}template<typename T>
my_shared_ptr<T>::my_shared_ptr(my_shared_ptr& other_ptr)
{this->m_ptr = other_ptr.m_ptr;this->share_count = other_ptr.share_count;addCount();
}template<typename T>
my_shared_ptr<T>& my_shared_ptr<T>::operator=(my_shared_ptr& other_ptr)
{this->m_ptr = other_ptr.m_ptr;this->share_count = other_ptr.share_count;addCount();return *this;
}template<typename T>
T&  my_shared_ptr<T>::operator*()
{return *this->m_ptr;
}template<typename T>
T*  my_shared_ptr<T>::operator->()
{return this->m_ptr;
}template<typename T>
void my_shared_ptr<T>::addCount()
{std::lock_guard<std::mutex> guard(gMutex);(*this->share_count)++;
}template<typename T>
void my_shared_ptr<T>::minusCount()
{std::lock_guard<std::mutex> guard(gMutex);(*this->share_count)--;
}template<typename T>
int my_shared_ptr<T>::user_count()
{return *this->share_count;
}// person.h
#include<string>class Person
{
public:Person(std::string name);Person(const Person& p);~Person();std::string& getName();private:std::string m_name;
};// person.cpp
#include "person.h"
#include<iostream>
Person::Person(std::string name):m_name(name)
{std::cout << "Person constructor name: " << m_name << std::endl;
}Person::Person(const Person& p)
{this->m_name = p.m_name;std::cout << "Person  copy constructor name: " << this->m_name << std::endl;
}Person::~Person()
{std::cout << "Person destructor name: " << m_name << std::endl;
}std::string& Person::getName()
{return m_name;
}// main.cppvoid  testMySharedPtr()
{my_shared_ptr<Person>  ptr1(new Person("ptr1 ---"));std::cout << "ptr1 user_count: " << ptr1.user_count() << std::endl;my_shared_ptr<Person> ptr2(ptr1);std::cout << "ptr1 user_count: " << ptr1.user_count() << std::endl;std::cout << "ptr2 user_count: " << ptr2.user_count() << std::endl;my_shared_ptr<Person> ptr3 = ptr2;std::cout << "ptr1 user_count: " << ptr1.user_count() << std::endl;std::cout << "ptr2 user_count: " << ptr2.user_count() << std::endl;std::cout << "ptr3 user_count: " << ptr3.user_count() << std::endl;}int main()
{testMySharedPtr();return 0;
}

输出

 

3. weak_ptr

前面提到过,weak_ptr 是与 shared_ptr 配合使用的,weak_ptr 无引用计数。

// my_weak_ptr.h
template<typename T>
class my_weak_ptr
{
public:my_weak_ptr(T* ptr = nullptr);~my_weak_ptr();my_weak_ptr(my_weak_ptr& other_ptr);my_weak_ptr& operator=(my_weak_ptr& other_ptr);T& operator*();T* operator->();private:T* m_ptr;
};template<typename T>
my_weak_ptr<T>::my_weak_ptr(T* ptr):m_ptr(ptr)
{}template<typename T>
my_weak_ptr<T>::~my_weak_ptr()
{if(m_ptr)delete m_ptr;
}template<typename T>
my_weak_ptr<T>::my_weak_ptr(my_weak_ptr& other_ptr)
{this->m_ptr = other_ptr.m_ptr;
}template<typename T>
my_weak_ptr<T>& my_weak_ptr<T>::operator=(my_weak_ptr& other_ptr)
{this->m_ptr = other_ptr.m_ptr;return *this;
}template<typename T>
T&  my_weak_ptr<T>::operator*()
{return *this->m_ptr;
}template<typename T>
T*  my_weak_ptr<T>::operator->()
{return this->m_ptr;
}// A.h
#include"my_weak_ptr.h"class B;class A
{
public:A();~A();public:my_weak_ptr<B> m_b_ptr;
};// A.cpp
A::A()
{std::cout << "A constructor ---" << std::endl;}A::~A()
{std::cout << "A destructor ---" << std::endl;
}// B.h
#include"my_shared_ptr.h"class A;class B
{
public:B();~B();public:my_shared_ptr<A> m_a_ptr;
};// B.cpp
#include "b.h"
#include<iostream>
B::B()
{std::cout << "B constructor ---" << std::endl;}B::~B()
{std::cout << "B destructor -- " << std::endl;
}// main.cppvoid testMyWeakPtr()
{my_shared_ptr<A> pa(new A);my_weak_ptr<B> pb(new B);pa->m_b_ptr = pb;pb->m_a_ptr = pa;}int main()
{testMyWeakPtr();return 0;
}

输出:

忽视其中 B 析构了两次,通过结果可以看到,A 与 B 均能够析构。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/90462.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Failed to execute goal org.apache.maven.plugins

原因&#xff1a; 这个文件D:\java\maven\com\ruoyi\pg-student\maven-metadata-local.xml出了问题 解决&#xff1a; 最简单的直接删除D:\java\maven\com\ruoyi\pg-student\maven-metadata-local.xml重新打包 或者把D:\java\maven\com\ruoyi\pg-student这个目录下所有文件…

汇编指令练习

1.大小比较&#xff08;循环&#xff09; start: /*mov r0,#0x9mov r1,#0xfb LoopLoop:cmp r0,r1beq stopsubhi r0,r0,r1subcc r1,r1,r0b Loop stop:b stop.end 仿真图 2. 1到100之和 start:mov r0,#0x1mov r1,#0x0b sum sum:add r1,r1,r0add r0,r0,#0x1cmp r0,#0x65beq sto…

记录每日LeetCode 617.合并二叉树 Java实现

题目描述&#xff1a; 给你两棵二叉树&#xff1a; root1 和 root2 。 想象一下&#xff0c;当你将其中一棵覆盖到另一棵之上时&#xff0c;两棵树上的一些节点将会重叠&#xff08;而另一些不会&#xff09;。你需要将这两棵树合并成一棵新二叉树。合并的规则是&#xff1a;…

Handler详解

跟Handler有关系的&#xff0c;包括Thread&#xff0c;Looper&#xff0c;Handler&#xff0c;MessageQueue Looper: 由于Looper是android包加入的类&#xff0c;而Thread是java包的类&#xff0c;所以&#xff0c;想要为Thread创建一个Looper&#xff0c;需要在线程内部调用…

iOS字体像素与磅的对应关系

注意&#xff1a;低于iOS10的系统&#xff0c;显示的字宽和字高比高于iOS10的系统小。 这就是iOS10系统发布时&#xff0c;很多app显示的内容后面出现…&#xff0c;因而出现很多app为了适配iOS10系统而重新发布新版本。 用PS设计的iOS效果图中&#xff0c;字体是以像素&#x…

添加vue devtools扩展工具+添加后F12不显示Vue图标

前言&#xff1a;在开启Vue学习之旅时&#xff0c;遇到问题两个问题&#xff0c;第一添加不上vue devtools扩展工具&#xff0c;第二添加完成后&#xff0c;F12不显示Vue图标。查阅了很多博客&#xff0c;自己解决了问题&#xff0c;故写此博客记录。如果你遇到和我一样的问题&…

在 Linux 虚拟机上使用 Azure 自定义脚本扩展版本

参考 azure创建虚拟机,创建虚拟机注意入站端口规则开放80端口、 2.转到资源&#xff0c;点击扩展应用程序&#xff0c;创建存储账户&#xff0c;创建容器&#xff0c;上传文件&#xff0c;选择文件&#xff0c;会自动执行部署。 apt-get update -y && apt-get insta…

计算机网络-物理层(二)- 传输方式

计算机网络-物理层&#xff08;二&#xff09;- 传输方式 串型传输与并行传输 串行传输:是指数据是一个比特一个比特依次发送的&#xff0c;因此在发送端和接收端之间&#xff0c;只需要一条数据传输线路即可 并行传输:是指一次发送n个比特而不是一个比特&#xff0c;因此发送…

计算机网络-物理层(一)物理层的概念与传输媒体

计算机网络-物理层&#xff08;一&#xff09;物理层的概念与传输媒体 物理层相关概念 物理层的作用用来解决在各种传输媒体上传输比特0和1的问题&#xff0c;进而为数据链路层提供透明(看不见)传输比特流的服务物理层为数据链路层屏蔽了各种传输媒体的差异&#xff0c;使数据…

django boostrap html实现可拖拽的左右布局,鼠标拖动调整左右布局的大小或占比

一、实现的效果 最近需要在Django项目中,实现一个左右布局的html页面,页面框架使用的是boostrap。但这个布局不是简单的左右分栏布局,而是需要实现可以通过鼠标拖拽的方式动态调整左右两侧布局的大小和占比。效果大致如下: 一开始,页面分为左右两块布局: 鼠标放到中间的…

Python第三方库 - Pandas库

文章目录 1. Pandas介绍2. Pandas基础2.1 引入2.2 数据结构2.2.1 Series2.3 DataFrame2.3.1 概念 3 Pandas - CSV 文件3.1 语法3.2 遇到的问题 4 Pandas - JSON4.1 语法 参考文档 1. Pandas介绍 概念: Pandas 是 Python 的核心数据分析支持库&#xff0c;提供了快速、灵活、明…

Tomcat日志中文乱码

修改安装目录下的日志配置 D:\ProgramFiles\apache-tomcat-9.0.78\conf\logging.properties java.util.logging.ConsoleHandler.encoding GBK

Spring Security6 最新版配置该怎么写,该如何实现动态权限管理

Spring Security 在最近几个版本中配置的写法都有一些变化&#xff0c;很多常见的方法都废弃了&#xff0c;并且将在未来的 Spring Security7 中移除&#xff0c;因此又补充了一些新的内容&#xff0c;重新发一下&#xff0c;供各位使用 Spring Security 的小伙伴们参考。 接下…

若依框架浅浅介绍

由若依官网所给介绍可知 1、文件结构介绍 在ruoyi-admin的pom.xml文件中引入了ruoyi-framework、ruoyi-quartz和ruoyi-generatior模块&#xff0c;在ruoyi-framework的pom.xml文件中引入了ruoyi-system模块。 2、技术栈介绍 前端&#xff1a;Vue、Element UI后端&#xff1a…

面试热题(缺失的第一个正数)

给你一个未排序的整数数组 nums &#xff0c;请你找出其中没有出现的最小的正整数。 请你实现时间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案。 输入&#xff1a;nums [1,2,0] 输出&#xff1a;3 尝试的路途是痛苦的&#xff0c;不断的尝试新方法&#xff0c;错何尝…

flask-----初始项目架构

1.初始的项目目录 -apps 包 ------存放app -user文件夹 -------就是一个app -models.py --------存放表模型 -views.py -------存放主代码 -ext包 -init.py -------实例化db对象 -manage.py -----运行项目的入口 -setting.py -----配置文件 2.各文件内容 manage…

vue里搜索框实现防抖功能

进来调用一个闭包函数debounce()&#xff0c;赋值给一个变量debounceFunc&#xff0c;&#xff08;包闭的功能就是说里面的变量timer和参数一直驻留在函数里面&#xff09; input事件调用一个函数debounceFunc&#xff08;&#xff09;&#xff0c;并且传一个回调searchs函数&a…

创建maven的Springboot项目出现错误:Cannot access alimaven

创建maven的Springboot项目出现错误&#xff1a;Cannot access alimaven 1&#xff09;问题2) 分析问题3&#xff09;解决问题 1&#xff09;问题 创建maven的Springboot项目出现错误&#xff1a; Cannot access alimaven (http://maven.aliyun.com/nexus/content/groups/p…

【Spring】深入探索 Spring AOP:概念、使用与实现原理解析

文章目录 前言一、初识 Spring AOP1.1 什么是 AOP1.2 什么是 Spring AOP 二、AOP 的核心概念2.1 切面&#xff08;Aspect&#xff09;2.2 切点&#xff08;Pointcut&#xff09;2.3 通知&#xff08;Advice&#xff09;2.4 连接点&#xff08;Join Point&#xff09; 三、Sprin…

【Linux进程篇】环境变量

【Linux进程篇】环境变量 目录 【Linux进程篇】环境变量基本概念常见环境变量查看环境变量方法测试PATH测试HOME测试SHELL和环境变量相关的命令环境变量的组织方式通过代码如何获取环境变量命令行参数命令行第三个参数通过第三方变量environ获取 本地变量通过系统调用获取或设置…