首页 > 编程语言 >C++智能指针

C++智能指针

时间:2024-03-25 13:23:31浏览次数:27  
标签:std p1 C++ 智能 shared unique ptr 指针

为什么需要智能指针

对于普通指针,在程序结束前我们需要将每个指针都进行free,以免造成内存泄漏。但是手动释放指针是麻烦的,并且一旦漏掉就会造成内存泄漏。因此在C++11中引入智能指针避免此种情况的发生。

智能指针包括std::shared_ptr/std::unique_ptr/std::weak_ptr,需要使用头文件<memory>

引用计数

引用计数是为了防止内存泄漏而引入的概念。想法是基于动态分配的对象,每当增加一次对同一对象的引用,那么引用对象的引用次数就会增加1,每删除一次就减1,当计数减为0时则自动释放指向的堆内存。

std::shared_ptr

std::shared_ptr是一种智能指针,能够记录多少个shared_ptr共同指向同一个对象,从而消除显式地调用delete,当计数变为0时将指针自动删除。

同样地,我们不希望显式地调用new从而使得达成对称,因此引入std::make_shared

#include <iostream>
#include <memory>

void foo(std::shared_ptr<int> i) {
    (*i)++;
}

int main() {
    auto p = std::make_shared<int>(10);
    foo(p);
    std::cout << *p << std::endl; // 11
}

get()可以获取原始指针,use_count()可以查看指针引用计数,reset()可以减少一个引用计数。

#include <iostream>
#include <memory>

int main() {
    auto pointer = std::make_shared<int>(10);
    auto pointer2 = pointer;
    auto pointer3 = pointer;
    int* p = pointer.get();
    std::cout << "pointer.use_count() = " << pointer.use_count() << std::endl;
    std::cout << "pointer2.use_count() = " << pointer2.use_count() << std::endl;
    std::cout << "pointer3.use_count() = " << pointer3.use_count() << std::endl;

    pointer2.reset();

    std::cout << "pointer2.reset()" << std::endl;
    std::cout << "pointer.use_count() = " << pointer.use_count() << std::endl;
    std::cout << "pointer2.use_count() = " << pointer2.use_count() << std::endl;
    std::cout << "pointer3.use_count() = " << pointer3.use_count() << std::endl;

    pointer3.reset();
    std::cout << "pointer3.reset()" << std::endl;
    std::cout << "pointer.use_count() = " << pointer.use_count() << std::endl;
    std::cout << "pointer2.use_count() = " << pointer2.use_count() << std::endl;
    std::cout << "pointer3.use_count() = " << pointer3.use_count() << std::endl;
}
/*
pointer.use_count() = 3
pointer2.use_count() = 3
pointer3.use_count() = 3
pointer2.reset()
pointer.use_count() = 2
pointer2.use_count() = 0
pointer3.use_count() = 2
pointer3.reset()
pointer.use_count() = 1
pointer2.use_count() = 0
pointer3.use_count() = 0
*/

std::unique_ptr

std::unique_ptr是一种独占的智能指针,禁止与其他智能指针共享一个对象,从而保证代码的安全。

#include <iostream>
#include <memory>

int main() {
    std::unique_ptr<int> pointer = std::make_unique<int>(10);
    auto pointer2 = pointer;
    std::cout << *pointer2 << std::endl;
}

很明显这样会报错

错误  C2280   “std::unique_ptr<int,std::default_delete<int>>::unique_ptr(const std::unique_ptr<int,std::default_delete<int>> &)”: 尝试引用已删除的函数  shared_pointer  C:\\Users\\hasee\\Desktop\\code\\test\\shared_pointer\\test_1.cpp  6

虽然std::unique_ptr是独占,不可转移,我们可以使用C++11中的std::move()转移给其他std::unique_ptr。转移并不是复制,例如p1转移给空指针p2后,p2变成p1p1变成空。

#include <iostream>
#include <memory>

struct Foo {
    Foo() { std::cout << "Foo::Foo" << std::endl; }
    ~Foo() { std::cout << "Foo::~Foo" << std::endl; }
    void foo() { std::cout << "Foo::foo" << std::endl; }
};

void f(const Foo &) {
    std::cout << "f(const Foo&)" << std::endl;
}

int main() {
    std::unique_ptr<Foo> p1(std::make_unique<Foo>());
    if (p1) p1->foo(); // 不空
    {
        std::unique_ptr<Foo> p2(std::move(p1));
        f(*p2); 
        if (p2) {
            p2->foo(); // 不空
        }
        if (p1) {
            p1->foo();
        }
        p1 = std::move(p2); // 空
        if (p2) {
            p2->foo(); // 空
        }
        std::cout << "p2 gg" << std::endl;
    }
    if (p1) {
        p1->foo();
    }
}

std::weak_ptr

std::shared_ptr虽然相比普通指针优化了内存泄漏的情况,但是仍然存在内存泄漏的可能。

仔细观察可以发现std::shared_ptr通过引用计数销毁指针是做了一个根据依赖关系类似拓扑排序的删点过程,那么也就是说如果依赖关系存在环,那么std::shared_ptr同样无法释放资源,造成内存泄漏。

因此引入std::weak_ptr,弱引用不会使引用计数增加,因此在析构时可以销毁。

std::weak_ptr没有*->运算符,因此不能对资源进行操作,而可以用于检查std::shared_ptr是否存在。

std::weak_ptr通常不单独使用,和std::shared_ptr搭配使用,是一种辅助工具。

内容主要来自https://changkun.de/modern-cpp/zh-cn/05-pointers/

标签:std,p1,C++,智能,shared,unique,ptr,指针
From: https://www.cnblogs.com/lgats324/p/18094152

相关文章

  • L2-019 悄悄关注 (25分) c++代码
    新浪微博上有个“悄悄关注”,一个用户悄悄关注的人,不出现在这个用户的关注列表上,但系统会推送其悄悄关注的人发表的微博给该用户。现在我们来做一回网络侦探,根据某人的关注列表和其对其他用户的点赞情况,扒出有可能被其悄悄关注的人。输入格式:输入首先在第一行给出某用户的关注......
  • L2-023 图着色问题(25分) c++代码
    还是别把问题想复杂了。。图着色问题是一个著名的NP完全问题。给定无向图G=(V,E),问可否用K种颜色为V中的每一个顶点分配一种颜色,使得不会有两个相邻顶点具有同一种颜色?但本题并不是要你解决这个着色问题,而是对给定的一种颜色分配,请你判断这是否是图着色问题的一个解。输入格......
  • L2-021 点赞狂魔(25分) c++代码
    微博上有个“点赞”功能,你可以为你喜欢的博文点个赞表示支持。每篇博文都有一些刻画其特性的标签,而你点赞的博文的类型,也间接刻画了你的特性。然而有这么一种人,他们会通过给自己看到的一切内容点赞来狂刷存在感,这种人就被称为“点赞狂魔”。他们点赞的标签非常分散,无法体现出明......
  • L2-022 重排链表(25分) c++代码
    给定一个单链表 L1​→L2​→⋯→Ln−1​→Ln​,请编写程序将链表重新排列为 Ln​→L1​→Ln−1​→L2​→⋯。例如:给定L为1→2→3→4→5→6,则输出应该为6→1→5→2→4→3。输入格式:每个输入包含1个测试用例。每个测试用例第1行给出第1个结点的地址和结点总个数,即正整数N (......
  • 百度【灵境矩阵】智能体开发初学笔记
    AIAgent(人工智能代理)是一种能够感知环境、进行决策和执行动作的智能实体。AIAgent可以称为“智能体”,也可以理解为“智能业务助理”,指在大模型技术驱动下,让人们以自然语言为交互方式高自动化地执行和处理专业或繁复的工作任务,从而极大程度释放人员精力。灵境矩阵是百度推出的......
  • Base64编解码及C++代码实现
    1.Base64是什么?        Base64是一种二进制到文本的编码方式。如果要更具体一点的话,可以认为它是一种将byte数组编码为字符串的方法,而且编码出的字符串只包含ASCII基础字符。        例如字符串mickey0380对应的Base64为bWlja2V5MDM4MA==。其中那个=......
  • C++ | 剪枝(DFS)lanqiao OJ 2942
     上一篇我们已经分享了DFS的学习,剪枝相当于对部分DFS进行优化正常用DFS写,会遍历每一种情况,因此要判断他的合法性,并且在第十个检测点会超时,用剪枝后,这道题就可以过啦。//不剪枝的方法#include<bits/stdc++.h>usingnamespacestd;constintN=15;inta[N],n;v......
  • C++类的构造函数和析构函数
    提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档文章目录1.构造函数(Constructor)2.析构函数(Destructor):3.构造函数与析构函数的调用顺序:4.注意事项:5.示例总结1.构造函数(Constructor)**定义:**构造函数是一种特殊的成员函数,用于在创建对象时初始化......
  • 鼎信:输电线路智能激光雷达点云监测装置|助力电力部门巡检
    近几年,随着城市建设突飞猛进,输电线路的空间愈发局促,不时出现的故障让人捏了一把汗。五一小长假临近,各地的电力部门都在为输电线路做全面体检,特别是那些“三跨”区段、交叉跨越点,以及施工繁忙的区域,都是巡检的重点。但在这其中,外破易发区更是重中之重,安全隐患的排查工作刻不容缓......
  • 人工智能的渐进奇迹:大语言模型逐步揭秘
    两年前,在一个名为"超越模仿游戏基准"(BIG-bench)的项目中,450名研究人员汇编了204个旨在测试大型语言模型能力的任务清单,这些模型是像ChatGPT这样的聊天机器人的动力源泉。在大多数任务上,随着模型规模的增加,性能预测地平稳提高——模型越大,性能越好。但在其他任务上,能力的提升并......