网站做的好的公司有,青岛网站建设在哪,建立一个商城网站,寺庙招人做网站维护吗C 智能指针常用总结 文章目录 C 智能指针常用总结1. 写在对前面2. why 智能指针3. what 智能指针3.1 unique_ptr3.2 shared_ptr3.3 weak_ptr 3. how 指针指针3.1 unique_ptr3.1.1 创建3.1.2 成员函数 3.2 shared_ptr3.2.1创建3.2.2 成员对象 3.3 weak_ptr 4. 碎碎念5.参考资料 …C 智能指针常用总结 文章目录 C 智能指针常用总结1. 写在对前面2. why 智能指针3. what 智能指针3.1 unique_ptr3.2 shared_ptr3.3 weak_ptr 3. how 指针指针3.1 unique_ptr3.1.1 创建3.1.2 成员函数 3.2 shared_ptr3.2.1创建3.2.2 成员对象 3.3 weak_ptr 4. 碎碎念5.参考资料 1. 写在对前面
最近接了一个写 c 的任务在使用智能指针的时候写出一个 dump 的 bug。作为 golang 小白在入门 c 的必经之路。整理一份 c 智能指针的常用知识点。全文按照如下思路整理 why 智能指针 what 智能指针 how 智能指针
智能指针是 C 中用于管理动态分配对象的一种特殊指针。它能够自动管理对象的生命周期。避免内存泄漏和野指针的问题。
2. why 智能指针
「一种特性的提出势必是为了解决某类难以解决的问题。」在智能指针提出之前C 是通过手动分配和释放内存来进行管理的。使用如下两个关键字进行分配和释放内存 new用于分配动态内存。通过使用 new 关键字可以在堆上分配指定大小的内存并返回指向新分配的内存的指针。例如可以使用 int* p new int; 来分配一个整数的内存并将指向该内存的指针存储在变量 p 中。 delete用于释放动态内存。使用 delete 关键字可以释放通过 new 关键字分配的内存。例如可以使用 delete p; 来释放变量 p 指向的整数内存。
这种原始的内存管理方式需要程序员手动追踪和管理分配和释放的内存容易引发一系列问题比如内存泄漏、重复释放、野指针等。因此为了简化内存管理的过程并减少错误C 引入智能指针和 RAII (资源获取即初始化) 等方式来自动管理和释放内存。 注资源获取即初始化(Resource Acquisition Is InitializationRAII) 是一种 C 编程技术通过在对象的构造函数中获取资源并在析构函数中释放资源以确保资源在对象生命周期内始终被争取管理。 RAII 的核心思想将资源的生命周期与对象的生命周期绑定在一起。当对象被创建时它获取了所需的资源当对象被销毁时它释放了持有的资源。 3. what 智能指针
智能指针有多种实现包括 std::shared_ptr、std::unique_ptr 和 std::weak_ptr。要理解这三类智能指针需要从「对象所有权」的概念入手。 每个独占的资源都应该有一个所有者 owner。 被共享的资源需要通过引用计数来管理。对应的实现是 shared_ptr。 借用资源的时候可以使用 raw point 或者引用。但更推荐使用引用 如果有循环引用需要打破循环可能的办法有借助 weak pointer。 资源的所有权可以被转移比如 move。 资源的所有者负责释放资源资源的借用者不能释放资源。
指针解决的是第1、2、4 这三类问题。
3.1 unique_ptr
unique_ptr 代表的是专属所有权exclusive ownership即由 unique_ptr 管理的内存只能被一个对象持有。 注大多数场景下应用到的应该是 unique_ptr 性能c 的 zero cost abstraction 的特点unique_ptr 在默认的情况下和裸指针的大小是一样的。所以在内存上没有任何的额外消耗性能最优。
3.2 shared_ptr
shared_ptr 代表共享所有权shared ownership即多个 shared_ptr 可以共享同一块内存。
性能 「内存占用高」shared_ptr 的内存占用是裸指针的两倍。因为除了要管理一个裸指针外还需要维护一个引用计数。 「原子操作性能低」考虑到线程安全问题引用计数的增减必须是原子操作。而原子操作一般情况下都比非原子操作慢。 「使用移动优化性能」shared_ptr 在性能是虽然低于 unique_ptr。但是可以通过使用 std::move 来优化性能。在「复制 a b」与「移动a std::move(b)」之间建议选择后者。因为移动不用增加引用计数。
3.3 weak_ptr
weak_ptr 解决了 shared_ptr 存在的「循环引用」问题。它不会增加引用计数也不会阻止资源的销毁。
避免循环引用在对象之间存在循环引用时使用 shared_ptr 会到导致资源无法释放。而 weak_ptr 可以打破循环引用因为它不增加引用计数。当所有的 shared_ptr 释放之后资源会被争取的释放。
导致循环引用的示例
class B;
struct A{shared_ptrB b;
};
struct B{shared_ptrA a;
};
auto pa make_sharedA();
auto pb make_sharedB();
pa-b pb;
pb-a pa;注解除循环引用只需要 struct B 中的 shared_ptr(A) 替换为 weak_ptr(A) 性能weak_ptr 具有比 shared_ptr 更高的性能。但是需要注意的是由于 weak_ptr 不会增加引用计数所以如果想要访问对象需要提前判断对象是否存在防止访问空指针。
3. how 指针指针
3.1 unique_ptr
3.1.1 创建
可以通过如下两种方式构造出 unique_ptr 类型的空指针
std::unique_ptrint p1; // 不传入任何实参
std::unique_ptrint p2(nullptr); // 传入空指针在构建 unique_ptr 智能指针时明确其指向
std::unique_ptrint p3(new int(20));使用 std::make_unique 模板函数可以用于初始化 unique_ptr 智能指针
std::unique_ptrint p4 std::make_uniqueint(10);注std::make_unique 为 C11 标准提供的函数 使用 std::move 移动构造函数
std::unique_ptrint p5(std::move(p4))
std::unique_ptrint p5 std::move(p4)注使用 std::move(p4) 初始化 p5会使得 p5 拥有 p4 的堆内存而 p4 则变成空智能指针 在初始化 unique_ptr 智能指针时还可以自定义所指堆内存的释放规则可以在定义的释放规则中加入自定义的释放规则以释放默写资源
//指定 default_delete 作为释放规则
std::unique_ptrint p6(new int[10], std::default_deleteint[]());//自定义释放规则
void deleteInt(int*p) {delete []p;
}// lambda 表示缩写 deleteInt 函数
// [](int* p) {delete[]p; }//初始化智能指针并自定义释放规则
std::unique_ptrint p7(new int[10], deleteInt);特殊说明unique_ptr 不支持复制和赋值
auto w std::make_uniqueint();
auto w2 w; // 编译错误3.1.2 成员函数
成员方法名功 能operator()重载赋值号operator*()重载 * 号获取当前unique_ptr 智能指针对象指向的数据。operator-()重载 - 号当智能指针指向的数据类型为自定义的结构体时通过 - 运算符可以获取其内部的指定成员。release()返回指向管理对象的指针并释放所有权。reset()替换 unique_ptr 管理的对象。swap()交换 2 个相同类型 unique_ptr 智能指针的内容。get()获得 unique_ptr 对象内部包含的普通指针。get_deleter()返回用于销毁管理对象的删除器operator bool()判断当前 unique_ptr 对象是否为空智能指针如果是空指针返回 false反之返回 true。
3.2 shared_ptr
3.2.1创建 可以通过如下两种方式构造出 shared_ptr 类型的空指针 std::shared_ptrint p1; // 不传入任何实参
std::shared_ptrint p2(nullptr); // 传入空指针在构建 shared_ptr 智能指针时明确其指向 std::shared_ptrint p3(new int(20));使用 std::make_shared 模板函数可以用于初始化 shared_ptr 智能指针 std::shared_ptrint p4 std::make_sharedint(10);注std::make_shared 为 C11 标准提供的函数 使用 std::move 移动构造函数或者使用赋值构造 // 赋值构造
std::shared_ptrint p5(p4);
std::shared_ptrint p5 p4;// 移动构造
std::unique_ptrint p5(std::move(p4))
std::unique_ptrint p5 std::move(p4)注 使用赋值构造会导致引用计数加 1同一普通指针不能同时为多个 shared_ptr 对象赋值 使用 std::move(p4) 初始化 p5会使得 p5 拥有 p4 的堆内存而 p4 则变成空智能指针 在初始化 unique_ptr 智能指针时还可以自定义所指堆内存的释放规则可以在定义的释放规则中加入自定义的释放规则以释放默写资源 //指定 default_delete 作为释放规则
std::shared_ptrint p6(new int[10], std::default_deleteint[]());//自定义释放规则
void deleteInt(int*p) {delete []p;
}// lambda 表示缩写 deleteInt 函数
// [](int* p) {delete[]p; }//初始化智能指针并自定义释放规则
std::shared_ptrint p7(new int[10], deleteInt);3.2.2 成员对象
成员方法名功 能operator()重载赋值号使得同一类型的 shared_ptr 智能指针可以相互赋值。operator*()重载 * 号获取当前 shared_ptr 智能指针对象指向的数据。operator-()重载 - 号当智能指针指向的数据类型为自定义的结构体时通过 - 运算符可以获取其内部的指定成员。swap()交换 2 个相同类型 shared_ptr 智能指针的内容。reset()当函数没有实参时该函数会使当前 shared_ptr 所指堆内存的引用计数减 1同时将当前对象重置为一个空指针当为函数传递一个新申请的堆内存时则调用该函数的 shared_ptr 对象会获得该存储空间的所有权并且引用计数的初始值为 1。get()获得 shared_ptr 对象内部包含的普通指针。use_count()返回同当前 shared_ptr 对象包括它指向相同的所有 shared_ptr 对象的数量。unique()判断当前 shared_ptr 对象指向的堆内存是否不再有其它 shared_ptr 对象再指向它。operator bool()判断当前 shared_ptr 对象是否为空智能指针如果是空指针返回 false反之返回 true。
3.3 weak_ptr
weak_ptr 当前的代码库中使用的较少后续代码用到单独总结其使用场景吧。
4. 碎碎念
虽然现在总结的知识都可以通过 chatgpt 问到但是「学习」的目的在于自己学习、理解和分析如果只是单纯的看的话下次、下下次在遇到可能自己还是一知半解的照抄模式。 我永远喜欢我自己我有勇气敞开心扉去接受一起新的变化和想法但我永远不会因为别人的反映而惶恐不安不因外界的评价而自我怀疑我允许自己被否定但我无需认同也不会在意。 任何人在任何时间分道扬镳很正常。 如果感觉有点累了那就去喜欢的事物里喘口气。
5.参考资料 关于 c 智能指针的使用场景。智能指针能完全替代 new\delete 吗 什么是 move 理解 c value categories, move, move in Rust RAII: 如何编写没有内存泄漏的代码 with C std::unique_ptr - cppreference.com C11 shared_ptr智能指针超级详细 C转换构造函数将其它类型转换为当前类的类型 C拷贝构造函数复制构造函数详解 C 智能指针的正确使用方式 | 编程沉思录