Sirius' blog Sirius' blog
首页
  • 学习笔记

    • 《C++》
    • 《MATLAB》
    • 《Python》
  • 学习笔记

    • 《Git》
    • 《CMake》
  • 技术文档
  • 博客搭建
  • 学习
  • 友情链接
关于
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

Sirius0v0

怕什么真理无穷,进一寸有一寸的欢喜
首页
  • 学习笔记

    • 《C++》
    • 《MATLAB》
    • 《Python》
  • 学习笔记

    • 《Git》
    • 《CMake》
  • 技术文档
  • 博客搭建
  • 学习
  • 友情链接
关于
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 基础

  • Cpp小记
  • C++学习笔记-220705-V1
  • 进阶

    • CC++模板
    • CC++STL-常用容器
    • CC++STL-函数对象
    • CC++STL-常用算法
    • 现代C++:常量、变量与类型推导
    • 现代C++:函数式编程(lambda、函数对象包装器)
    • 现代C++:右值引用、移动语义与完美转发
    • 现代C++:智能指针
      • 共享指针shared_ptr
        • 使用共享指针
        • 常用方法说明
        • 自定义删除函数
      • 弱引用指针 weak_ptr
      • 独享指针 unique_ptr
        • 使用独享指针
        • 常用方法说明
        • 内存管理
        • 如何在函数间传递unique_ptr
    • C++11多线程编程
  • 奇思妙用

  • 库的使用

  • 《C++》学习笔记
  • 进阶
Sirius0v0
2023-01-13
目录

现代C++:智能指针

# 现代C++:智能指针

# 共享指针shared_ptr

共享指针会记录有多少个共享指针指向同一个物体,当数量为0便会自动释放内存。

# 使用共享指针

#include <memory>
using std::shared_ptr;

// 方法一【推荐】
// 使用make_shared动态分配内存
shared_ptr<int> p;
p = make_shared<int>(100);

// 方法二
// 使用new初始化
shared_ptr<int> p {new int(100)};
shared_ptr<int> p2 = p;
// ...DO SOMETHING

# 常用方法说明

# use_count()

使用use_count()可以查看有多少个shared_ptr指向某个物体;

# reset()

使用reset()会重置,不再指向原来的物体

# 自定义删除函数

默认使用delete来释放内存,同时可以使用自定义删除方式,如下是管理文件指针的示例:

void close_file(FILE* fp)
{
    if (fp == nullptr) return;
    fclose(fp);
    cout << "File Closed." << endl;
}

int main()
{
    FILE* fp = fopen("filepath.txt","w");
    // 管理文件指针,不是销毁指针变量,而是要关闭文件操作
    shared_ptr<FILE> sfp {fp, close_file};
    if (sfp == nullptr)
        cerr << "Error opening file." << endl;
    else
        cout << "File opened." << endl;
    return 0;
}

# 弱引用指针 weak_ptr

如下相互引用示例中,使用 shared_ptr 将会导致内存泄漏的情况

struct A;
struct B;

struct A {
    std::shared_ptr<B> ptrB;
    ~A() {
        std::cout << "A 被销毁" << std::endl;
    }
};
struct B {
    // std::weak_ptr<A> ptrA;
    std::shared_ptr<A> ptrA;
    ~B() {
        std::cout << "B 被销毁" << std::endl;
    }
};
void weak_ptr_test()
{
    auto a = std::make_shared<A>();
    auto b = std::make_shared<B>();
    a->ptrB = b;
    b->ptrA = a;
}

由于内部成员变量之间存在相互引用的情况,a,b引用计数均为2,当离开作用域,引用计数只能减1,然而外部已经无法找到该块内存,也就造成了内存泄漏,示意图如下:

解决这个问题的办法就是使用弱引用指针 std::weak_ptr ,std::weak_ptr 是一种弱引用,它不会引起引用计数增加,当换用弱引用时候,最终的释放流程如图:

在倒数第二步中由于B中指向A的引用为弱引用,不计入引用计数,因而B会被释放,此时A也没有指向的对象,因此这块内存资源也会被释放。

# 独享指针 unique_ptr

相比于shared_ptr,独享指针是一种零开销的智能指针

独享,因而不能有两个独享指针同时指向一份资源,也不支持复制操作

# 使用独享指针

#include <memory>
using namespace std;

// 方法一【推荐】
// 使用make_unique动态分配内存
unique_ptr<int> p {make_unique<int>(100)};

// 方法二
// 使用new初始化
unique_ptr<int> p {new int(100)};
unique_ptr<int> p2 = p;
// ...DO SOMETHING

# 常用方法说明

# reset()

释放unique_ptr下的资源

# 内存管理

默认情况下使用new和delete来分配和释放内存,

可以自定义分配函数和释放函数

// ...
int* my_alloc(int v)
{
    cout << "Allocating " << v << endl;
    return new int{v};
}

void my_dealloc(int* p)
{
    cout << "Deallocating " << *p << endl;
    delete p;
}

int main()
{
    unique_ptr<int, decltype(&my_dealloc)> cup {my_alloc(100), my_dealloc};
    return 0;
}

# 如何在函数间传递unique_ptr

由于独享指针不能复制操作这一特点,在函数传参时会进行复制操作进而编译报错,以下是几个解决方案

# 如果是访问独享指针指向的内容

void function1(int& value){}
void function2(int* p){}

void main()
{
    auto up = make_unique<int>(123);
    
    // 方法一
    // 传递所管理的资源给int引用
    function1(*up);
    
    // 方法二
    // 使用裸指针避免复制
    function2(up.get());
}

# 如果需要对独享指针指向的内容进行操作

void function3(unique_ptr<int>& up){}
void function4(unique_ptr<int> up){}

void main()
{
    auto up = make_unique<int>(123);
    
    // 方法一
    // 函数参数设置为unique_ptr的引用
    function3(up);
    
    // 方法二
    // 使用std::move()
    // 注意:转移控制权后原up被设置为nullptr
    function4(std::move(up));
}

# 函数返回unique_ptr

unique_ptr<int> return_uptr(int value)
{
    unique_ptr<int> up = make_unique<int>(value);
    return up;	// 这里原本是std::move(up),编译器自动替换
}

void main()
{
    unique_ptr<int> up = return_uptr(321);
    cout << "up: " << *up << endl;
}
编辑 (opens new window)
#Cpp
上次更新: 2023/08/16, 16:01:49
现代C++:右值引用、移动语义与完美转发
C++11多线程编程

← 现代C++:右值引用、移动语义与完美转发 C++11多线程编程→

最近更新
01
ipopt优化库配置及使用
07-21
02
ubuntu离线安装包的方法
07-21
03
其它控件的使用
03-05
更多文章>
Theme by Vdoing | Copyright © 2020-2024 Sirius0v0 | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式