智能指针(1)

news2024/11/22 11:13:10

智能指针(1)

  • 概念
    • 内存泄漏
    • 指针指针概念
    • RAII
    • 使用裸指针存在的问题
  • 智能指针使用
    • 分类
    • unique(唯一性智能指针)
      • 介绍
      • 智能指针的仿写
      • 代码理解
      • 删除器

概念

内存泄漏

内存泄漏:程序中已动态分配的堆内存由于某些原因而未释放或无法释放,导致程序运行速度减慢甚至崩溃。
内存泄漏分为两类:

  • 堆内存泄漏:在堆区申请了资源,结束时候没有将其堆区进行释放,导致该内存无法再次被使用。(或者说指向堆区内存的指针丢失)
  • 资源泄漏:通常指系统资源,比如套接字,文件描述符等,因为这些在系统中都是有限制的,如果创建了而不归还,长时间就会耗尽资源,导致其他程序不可用。(例如:创建文件文件描述符(打开文件),但不归还(不关闭文件))。

指针指针概念

在java和C#语言中都有自动的垃圾回收机制,系统可以自动释放资源,为此在这些语言中,内存管理都不是问题。
C++中没有 垃圾回收机制,必须自动取释放资源,否则就会出现内存泄漏,因为在开发中内存泄漏很容易出现,但是又必须耗费大量精力去寻找解决内存泄漏,所以出现了智能指针。

int main() {
	int* ip=new int(10);
	if(ip!=nullptr) {
	//.....
	}
	//因为代码处理复杂,忘记释放
	return 0;
}

如上面就会出现内存泄漏,而解决内存泄漏最有效的方法就是指针指针。
智能指针:智能指针就是一个对象,智能指针对象中有一个指针,该指针存储的动态动态船舰对象的地址,用于生存期的控制,能够确保指针指针在对象离开作用域是自动调用析构函数来销毁对象(释放内存),防止内存泄漏。使用了智能指针就不会担心内存泄漏的问题了,也不需要手动释放内存了。

RAII

RAII是资源获取即初始化,使局部对象来管理资源的技术称为资源获取即初始化。
改方法就是充分利用了C++局部对象自动销毁的特性来控制资源的生命周期。
这些资源主要指操作系统中内存,套接字等,局部对象是指存储在栈中的对象,其生命周期靠操作系统来管理,无需人工介入。
RAII过程四个步骤:

  • 设计一个类封装资源。
  • 在构造函数中初始化。
  • 在析构函数中执行销毁操作。
  • 使用时定义一个类的局部对象。
class Object {
    int* ip;
public:
    Object(int val = 0) {
        ip = new int(val);
        cout << "create obj" << endl;
    }
    ~Object() {
        delete ip;
        cout << "destory obj" << endl;
    }
};
void func() {
    Object obj(3);
    return;
}
int main() {
    func();
    return 0;
}

在func函数中创建了obj对象,obj也就是局部对象,在函数结束时,系统会自动释放销毁对象,也就自动调用了obj对象的析构函数。为此我们在使用资源的时候,在构造函数中进行初始化,在析构函数中进行销毁。

使用裸指针存在的问题

  • 难以区分指针指向的是单一对象还是一组对象
  • 使用完无法判断是否应该销毁对象,因为无法判断是否拥有指向的对象。(销毁之后仍然指向堆内存,但是堆内存已经释放)
  • 在已经确定了销毁指针的情况下,也无法确定是用那个关键字来删除,例如delete,fopen等
  • 即使确定了销毁指针的方法,也无法判断删除的对象是一组还是一个(delete,delete[])
  • 上面问题就算都解决了,也难以保证在代码所有执行路径中有且只有一次销毁指针。
  • 理论上没有办法来分辨一个指针是否处于悬挂状态。

智能指针使用

分类

C++11提供了4钟智能指针:auto_ptr,unique_ptr,shared_ptr和weak_ptr.
使用时需要引入头文件:#include < memory >
因为auto_ptr时C98标准中的,C98中没有右值引用,也没有move和forward,为此已经被C++11所弃用,所有此处不做过多介绍。
智能指针主要应用于多线程问题,因为在多线程中自主控制释放极为复杂,智能指针可以在线程结束时自动释放该对象的资源。

unique(唯一性智能指针)

介绍

我们以自定义的int类型指针为例:

int main() {
    unique_ptr<PtrInt> pa(new PtrInt(10));
    pa->Print();
    pa->SetValue(200);
    pa->Print();
    return 0;
}

我们创建了唯一性指针对象pa,指向船舰的PtrInt对象,可以通过指针来调用类的成员方法。并且会自动析构。
错误使用

my_unique_ptr<PtrInt> pa3(new PtrInt(3));//A
int main() {
    my_unique_ptr<PtrInt> pa(new PtrInt(10));
    PtrInt p(1);
    my_unique_ptr<PtrInt> pa1(&p);//B
    PtrInt* ip = new PtrInt(2);
    my_unique_ptr<PtrInt> pa2(ip);//C
}

A:使用智能指针管理全局变量是没有意义的,因为程序结束后,指针才销毁,都不如直接定义一个全局变量,其会在程序结束时自动销毁。(智能指针主要依赖局部对象来释放)
B:对象本身就是在栈中,而智能指针是处理堆区堆存,所以是错误的。
C:该方法本质上不是错误的,因为你用了裸指针,如果发现用了指针还没释放,就会释放两次,导致程序崩溃。
而这个唯一性指针是怎么实现的呢?

智能指针的仿写

template<class _Tp>
struct my_default_deleter
{
    void operator()(_Tp* ptr)const {
        delete ptr;
    }
};
template<class _Tp>
struct my_default_deleter<_Tp[]>
{
    void operator()(_Tp* ptr)const {
        delete []ptr;
    }
};
struct FileDeleter {
    void operator()(FILE* pf) const {
        fclose(pf);
    }
     
};
template<class _Tp,class _Dp=my_default_deleter<_Tp>> 
//template<class _Tp,class _Dp>
class my_unique_ptr//<_Tp[],_Dp>
{ 
public:
    using pointer = _Tp*;
    using elemtype_type = _Tp;
    using deleter_type = _Dp;
private:
    _Tp* ptr;
    deleter_type mDeleter;
public:
    /*deleter_type get_deleter()const {
        return mDeleter;
    }*/
    my_unique_ptr(_Tp* p = nullptr) :ptr(p) { cout << "create myuniqueptr" << endl; }//构造函数
    ~my_unique_ptr() {//析构
        mDeleter(ptr);//mDeleter.operator()(ptr);
        //get_deleter()(ptr);
        ptr = nullptr;
        cout << "deatory myuniqueptr" << endl;
    }
    my_unique_ptr(const my_unique_ptr&) = delete;//删除拷贝构造
    my_unique_ptr& operator=(const my_unique_ptr&) = delete;//删除左值赋值重载
    my_unique_ptr(my_unique_ptr&& right) :ptr(right.ptr) {//移动构造
        right.ptr = nullptr;
        cout << "move create myuniqueptr" << endl;
    }
    my_unique_ptr& operator=(my_unique_ptr&& right) {//右值赋值重载
        if (this == &right) return *this;
        reset(right.release());//首先返回参数对象的资源指针,然后用该指针指向的资源代替现有指针的资源
        return *this;
    }
    pointer get() const { return ptr; }//返回指针
    _Tp& operator*()const { return *get(); }//指针解引用
    pointer operator->()const { return get(); }//指向符号重载
    pointer release() {//资源返回,将指向资源的指针返回
        pointer p = ptr;
        ptr = nullptr;
        return p;
    }
    void reset(pointer p = nullptr) {//资源移动,将形参指针指向的资源覆盖掉原本的资源,释放原本资源。
        mDeleter(ptr);
        ptr = p;
    }
    void swap(my_unique_ptr& u) {//交换指针指向的资源
        std::swap(this->ptr, u.ptr);
    }
    operator bool() const {//判断是否指向有效空间
        return get() != nullptr?true:false;
    }
};

上面就是指针指针的仿写,下面是使用
指向符重载,解引用:可以看出智能指针在这一块的使用和裸指针相同

int main() {
    my_unique_ptr<PtrInt> pa(new PtrInt(10));
    (*pa).SetValue(100);
    (*pa).Print();
    pa->SetValue(200);
    pa->Print();
    //pa.operator->()->Print();此处系统省去了一个指向符。
    return 0;
}

reset函数和release函数
release():返回指针
reset():资源覆盖,释放原本资源

int main() {
    my_unique_ptr<PtrInt> pa(new PtrInt(10));
    pa->Print();
    pa.reset(new PtrInt(100));//用新资源覆盖旧资源
    pa->Print();
}
int main() {
    my_unique_ptr<PtrInt> pa(new PtrInt(10));
    PtrInt* p = pa.get();//获取了指针,
    delete p;//error
    PtrInt* ip = pa.release();//直接将指针指向nullptr,并且将指向资源的指针返回。
}

swap()函数:智能交换具名对象

int main() {
    my_unique_ptr<PtrInt> pa(new PtrInt(10));
    my_unique_ptr<PtrInt> pb(new PtrInt(20));
    pa->Print();
    pb->Print();
    pa.swap(pb);//只能和具名对象进行交换
    pa->Print();
    pb->Print();
    return 0;
}

判空
判断指针是否为nullptr

int main() {
    unique_ptr<PtrInt> pa(new PtrInt(10));
    unique_ptr<PtrInt> pb(new PtrInt(20));
    my_unique_ptr<PtrInt> pc;
    if (pc) {//判断是否指向有效空间
        pc->Print();
    }
}

移动拷贝,移动赋值,拷贝构造,赋值重载
因为赋值重载和拷贝构造都是默认的浅拷贝,所以会出现两个指针指向同一资源,导致释放两次出现错误,所以呢删除了系统默认的拷贝构造函数和赋值重载。
而移动构造和移动赋值是可以实现的。这也就是唯一性智能指针的特点,有且只能管理一份资源。

int main() {
    my_unique_ptr<PtrInt> pa(new PtrInt(10));
    //my_unique_ptr<PtrInt> pb(pa);//浅拷贝 error
    my_unique_ptr<PtrInt> pb(new PtrInt(20));
    //pa = pb;//浅拷贝 err
    my_unique_ptr<PtrInt> pc(std::move(pa));
    pa = std::move(pb);
    return 0;
}

代码理解

my_unique_ptr<PtrInt> func(int x) {
    my_unique_ptr<PtrInt> pa(new PtrInt(x));
    return pa;
}
int main() {
    my_unique_ptr<PtrInt> pa(new PtrInt(10));
    pa = func(100);
    return 0;
}

首先在主函数中创建了只能指针对象,然后在func函数中船舰了智能指针的另一个对象,函数结束时使用移动构造来构建一个将亡值对象,然后移动赋值给pa对象。
在这里插入图片描述

void func(my_unique_ptr<PtrInt> px) {
}
int main() {
    my_unique_ptr<PtrInt> pa(new PtrInt(10));
    func(std::move(pa));
    pa->Print();
    return 0;
}

该程序会崩掉,因为把pa的资源移动到了px参数中,不存在拷贝构造,所以程序会因为pa指向为nullptr而崩掉。

void func(my_unique_ptr<PtrInt>& px) {
    px->Print();
    px->SetValue(200);
    px->Print();
    px.release();
    //my_unique_ptr<PtrInt> pb(std::move(px));
}
int main() {
    my_unique_ptr<PtrInt> pa(new PtrInt(10));
    func(pa);
    pa->Print();//error
    return 0;
}

不能用移动语义接收智能指针,可以通过引用来接收智能指针,但不要在函数内部让让智能指针的资源移动走,否则智能指针指向nullptr。唯一性指针智能控制一个对象的资源。

删除器

在上面代码仿写中仿写了删除器,主要是为了使程序可用性更高,用到了仿函数,大家可以看一看以前的文章理解一下。

int main() {
    my_unique_ptr<FILE,FileDeleter> pfile(fopen("D:/tulun/ConsoleApplication6/ConsoleApplication6.cpp", "w"));//D:\tulun\ConsoleApplication6\ConsoleApplication6.cpp
    return 0;
}

结果如下:

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Vue.js 中的插槽是什么?如何使用插槽?

Vue.js 中的插槽是什么&#xff1f;如何使用插槽&#xff1f; 在 Vue.js 中&#xff0c;插槽是一种组件之间通信的机制&#xff0c;允许父组件向子组件传递内容&#xff0c;并在子组件中进行渲染。本文将介绍 Vue.js 中插槽的概念、优势以及如何使用插槽。 什么是插槽&#xf…

cas 6 单点登录登出管理

cas自带的登出是通过登出地址后面接的service地址进行跳转&#xff0c;但是对于service没有进行验证&#xff0c;这边我们网络渗透测试后说可能被钓鱼需要进行验证所以开始了以下操作。 1找资料 首先到cas官网找&#xff0c;发现项目有自带的是否跳转&#xff0c;跳转地址参数…

辞了外包,上岸字节测试岗我落泪了,400多个日夜没人知道我付出了多少....

前言&#xff1a; 没有绝对的天才&#xff0c;只有持续不断的付出。对于我们每一个平凡人来说&#xff0c;改变命运只能依靠努力幸运&#xff0c;但如果你不够幸运&#xff0c;那就只能拉高努力的占比。 2023年3月&#xff0c;我有幸成为了字节跳动的一名自动化测试工程师&am…

Qt导出pdf格式表格

预期目标如下&#xff1a; 头文件&#xff1a; #include #include #include #include #include #include #include private: QString m_html; 调用&#xff1a; QDateTime dateTime QDateTime::currentDateTime(); //当前日期和时间 QString ExportTime dateTime.t…

python带你获取小破站喜爱UP得所用内容

前言 嗨喽&#xff0c;大家好呀~这里是爱看美女的茜茜呐 环境使用: Python 3.8 Pycharm 2022.3版本 ffmpeg <需要设置环境变量> 软件的使用 合成视频和音频 模块使用: 第三方模块&#xff0c;需要安装 import requests >>> pip install requests 内置模…

认识数据湖加速器(Data Lake Accelerator Goose FileSystem,GooseFS)

认识数据湖加速器Data Lake Accelerator Goose FileSystem&#xff0c;GooseFS 一、产品概述二、产品功能三、产品优势四、快速入门五、使用 GooseFS 预热 Table 中的数据六、使用 GooseFS 进行文件上传和下载操作七、使用 GooseFS 加速文件上传和下载操作八、关闭 GooseFS九、…

UE5 Chaos破碎系统学习2

本文继续从实用性的角度学习Chaos破碎系统&#xff0c;因为破碎的许多操作需要力场&#xff0c;比较麻烦&#xff0c;因此本文打算绕过力场实现一些效果&#xff1a; 1.显示材质效果 制作Chaos破碎效果时&#xff0c;会在编辑器下看不见材质&#xff0c;我们可以选择Geometr…

美创数据安全服务能力再获认可!

美创数据安全服务能力又一次获认可&#xff01; 近日&#xff0c;经全方位能力评估和专家评审&#xff0c;美创获得中国软件评测中心和中国计算机行业协会数据安全专业委员会联合颁发的《数据安全服务能力评定资格证书》&#xff0c;数据安全评估能力符合二级评定资格要求。 为…

生存压力下,Smartbi这套方案为企业降本增效带来新的希望

在如今的经济环境下&#xff0c;许多IT企业都面临着困境。经济的不景气导致市场竞争更加激烈&#xff0c;企业的盈利能力受到了严重的冲击&#xff1b;高昂的成本让企业喘不上气来。为了在这个艰难的时期生存下来&#xff0c;降本增效成为了企业的当务之急。 l实施项目利润低&a…

【C++】是内存管理,但C++ !! 模板初阶

目录 一&#xff0c;回望C语言内存 二&#xff0c; C 内存管理方式 1. 内置类型 2. 自定义类型 3. new & malloc 返回内容区别 4. operator new & operator delete 5. malloc/free和new/delete的区别总结 6. 定位new表达式(placement-new) &#xff08;了…

018+limou+C语言预处理

0.前言 您好&#xff0c;这里是limou3434的一篇博客&#xff0c;感兴趣您可以看看我的其他博文系列。本次我主要给您带来了C语言有关预处理的知识。 1.宏的深度理解与使用 1.1.数值宏常量 #define PI 3.1415926注意define和#之间是可以留有空格的 1.2.字符宏常量 #includ…

是德DSO9254A示波器/KEYSIGHT DSO9254A:2.5 GHz

KEYSIGHT是德DSO9254A示波器&#xff0c;Infiniium 9000 系列 2.5 GHz 示波器提供 4 个模拟通道、10 Mpts 存储器和 20 GSa/s 采样率。 简介 Keysight(原Agilent) Infiniium DSO9254A 配有 15 英寸 XGA 显示屏&#xff0c;而且包装非常轻巧&#xff0c;仅有 9 英寸深、26 磅重…

C++ 编写二维码(有源码)

首先来展示一下成果&#xff1a; 二维码图片好像违规了&#xff0c;直接给链接吧网址链接 如果你扫了这个二维码就会得到一个网址&#xff0c;该网址是我写代码的参考&#xff0c;该网站讲述了如何编写一个二维码&#xff0c;很详细&#xff0c;我没有实现汉字的编码&#xff…

LeetCode ! 42 Trapping Rain Water

参考资料&#xff1a;leetCode评论区大佬, 《程序员代码面试指南》 思路1&#xff1a;使用单调栈 维持一个从栈底到栈顶中的元素——下标&#xff0c;对应到数组是从大到小排序。 遍历数组&#xff0c;如果新值大于栈顶元素&#xff08;下标&#xff09;对应的数组值&#xf…

『Linux』第九讲:Linux多线程详解(六 - 完结)_ 线程池 | 读写锁

「前言」文章是关于Linux多线程方面的知识&#xff0c;上一篇是 Linux多线程详解&#xff08;五&#xff09;&#xff0c;今天这篇是 Linux多线程详解&#xff08;六&#xff09;&#xff0c;也是多线程最后一篇&#xff0c;内容大致是线程池&#xff0c;讲解下面开始&#xff…

什么?英语不好?这所211可以不考英语!

本期为大家整理热门院校“哈尔滨工程大学810”的择校分析&#xff0c;这个择校分析专题会为大家结合&#xff1a;初试复试占比、复试录取规则&#xff08;是否公平&#xff09;、往年录取录取名单、招生人数、分数线、专业课难度等进行分析。希望能够帮到大家! –所有数据来源…

KDJJC-80绝缘油介电强度测试仪

一、概述 测试仪&#xff08;单杯&#xff09;是我公司科研技术人员&#xff0c;依据国家标准GB507-1986及行标DL/T846.7-2004的有关规定&#xff0c;发挥自身优势&#xff0c;经过多次现场试验和长期不懈努力&#xff0c;精心研制开发的高准确度、数字化工业仪器。 为满足不同…

初步了解SpringCloud微服务架构

✅作者简介&#xff1a;大家好&#xff0c;我是Cisyam&#xff0c;热爱Java后端开发者&#xff0c;一个想要与大家共同进步的男人&#x1f609;&#x1f609; &#x1f34e;个人主页&#xff1a;Cisyam-Shark的博客 &#x1f49e;当前专栏&#xff1a; 微服务探索之旅 ✨特色专…

Apikit SaaS 10.9.0 版本更新:接口测试支持通过 URL 请求大型文件,覆盖更多场景的文件请求测试

Hi&#xff0c;大家好&#xff01; Eolink Apikit 即将在 2023年 6月 8日晚 18:00 开始更新 10.9.0 版本。本次版本更新主要是对多个应用级资源合并&#xff0c;并基于此简化付费套餐和降低费率。 本次应用合并是为了接下来更好的发挥 Eolink Apikit 的优势&#xff0c;提供 …

Web前端-React学习

React基础 React 概述 React 是一个用于构建用户界面的JavaScript库。 用户界面&#xff1a; HTML页面&#xff08;前端&#xff09; React主要用来写HTML页面&#xff0c; 或构建Web应用 如果从MVC的角度来看&#xff0c;React仅仅是视图层&#xff08;V&#xff09;,也就…