IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C++智能指针使用指南 -> 正文阅读

[C++知识库]C++智能指针使用指南

智能指针是代理模式的具体应用,它使用 RAII 技术代理了裸指针,能够自动释放内存, 无需程序员干预,所以被称为“智能指针”。

智能指针不是指针,而是一个对象,所以不要对其调用delete,它会自动管理初始化时的指针,在离开作用域时析构释放内存。

智能指针也没有定义加减运算,不能随意移动指针地址,这样避免了指针越界操作。

在使用上:

如果指针是“独占”使用,就应该选择 unique_ptr,它为裸指针添加了很多限制,更加安全 。

如果指针是“共享”使用,就应该选择 shared_ptr,它的功能非常完善,用法几乎与原始指针一样

使用智能指针要加头文件#include <memory>

工厂函数make_unique()、make_shared()不只是返回智能指针对象,其内部也有优化。

如果你已经理解了智能指针,就尽量不要再使用裸指针、new 和 delete 来操作内存了。

unique_ptr

unique_ptr需要手动初始化,声明的时候必须用模板参数指定类型:

unique_ptr<int> ptr1(new int(10)); // int智能指针
assert(*ptr1 = 10); // 可以使用*取内容
assert(ptr1 != nullptr); // 可以判断是否为空指针
unique_ptr<string> ptr2(new string("hello")); // string智能指针
assert(*ptr2 == "hello"); // 可以使用*取内容
assert(ptr2->size() == 5); // 可以使用->调用成员函数

也可以调用工厂函数,强制创建智能指针的时候必须初始化:

auto ptr3 = make_unique<int>(42); // 工厂函数创建智能指针
assert(ptr3 && *ptr3 == 42);
auto ptr4 = make_unique<string>("god of war"); // 工厂函数创建智能指针
assert(!ptr4->empty());

unique_ptr表示该智能指针的所有权是唯一的,不允许共享,任何时候只能有一个人持有。

它禁止拷贝赋值,但是可以使用std::move()显式地声明所有权转移:

auto ptr1 = make_unique<int>(42); // 工厂函数创建智能指针
assert(ptr1 && *ptr1 == 42); // 此时智能指针有效
auto ptr2 = ptr1; // 编译有问题
auto ptr2 = std::move(ptr1); // 使用move()转移所有权
assert(ptr2 && *ptr2 == 42);
assert(ptr1); // 此时智能指针无效.会报错

指针的所有权就被转走了,原来的 unique_ptr 变成了空指针,新的 unique_ptr 接替了管理权,保证所有权的唯一性

shared_ptr

基本使用方式与unique_ptr并无不同:

shared_ptr<int> ptr1(new int(10)); // int智能指针
assert(*ptr1 = 10); // 可以使用*取内容
shared_ptr<string> ptr2(new string("hello")); // string智能指针
assert(*ptr2 == "hello"); // 可以使用*取内容
auto ptr3 = make_shared<int>(42); // 工厂函数创建智能指针
assert(ptr3 && *ptr3 == 42); // 可以判断是否为空指针
auto ptr4 = make_shared<string>("zelda"); // 工厂函数创建智能指针
assert(!ptr4->empty()); // 可以使用->调用成员函数

不过它的所有权可以被安全共享,支持拷贝赋值

auto ptr1 = make_shared<int>(42); // 工厂函数创建智能指针
assert(ptr1 && ptr1.unique() ); // 此时智能指针有效且唯一
auto ptr2 = ptr1; // 直接拷贝赋值,不需要使用move()
assert(ptr1 && ptr2); // 此时两个智能指针均有效
assert(ptr1 == ptr2); // shared_ptr可以直接比较
// 两个智能指针均不唯一,且引用计数为2
assert(!ptr1.unique() && ptr1.use_count() == 2);
assert(!ptr2.unique() && ptr2.use_count() == 2);

其内部使用引用计数,所以具有完整的”值语义“,可以在任何场合下代替原始指针。

不过维护引用计数的存储和管理都是成本,过度使用会降低运行效率。其引用计数也会带来循环引用,下面是简化后的典型例子:

#include <iostream>
#include <memory>
#include <assert.h>
using namespace std;
class Node final {
public:
    using this_type = Node;
    using shared_type = std::shared_ptr<this_type>;
    shared_type next;   // 使用只能指针来指向下一个节点
};
int main() {
    auto n1 = make_shared<Node>();  // 工厂函数创建智能指针
    auto n2 = make_shared<Node>();

    // 此时引用计数均为1
    assert(n1.use_count() == 1);
    assert(n2.use_count() == 1);

    // 产生循环引用
    n1->next = n2;
    n2->next = n1;

    // 此时引用计数均为2,且无法减到0,内存泄露
    assert(n1.use_count() == 2);
    assert(n2.use_count() == 2);
}

这个例子很简单,你一下子就能看出存在循环引用。但在实际开发中,指针的关系可不像例 子那么清晰,很有可能会不知不觉形成一个链条很长的循环引用,复杂到你根本无法识别, 想要找出来基本上是不可能的。 想要从根本上杜绝循环引用,光靠 shared_ptr 是不行了,必须要用到weak_ptr

weak_ptr

它专门为打破循环引用而设计,只观察指针,不会增 加引用计数(弱引用),但在需要的时候,可以调用成员函数 lock(),获取 shared_ptr(强引用) 。用法如下

#include <iostream>
#include <memory>
#include <assert.h>
using namespace std;
class Node final {
public:
    using this_type = Node;
    // 改用weak_ptr
    using shared_type = std::weak_ptr<this_type>;
    shared_type next;   // 使用只能指针来指向下一个节点
};
int main() {
    auto n1 = make_shared<Node>();  // 工厂函数创建智能指针
    auto n2 = make_shared<Node>();

    // 此时引用计数均为1
    assert(n1.use_count() == 1);
    assert(n2.use_count() == 1);

    // 产生循环引用
    n1->next = n2;
    n2->next = n1;

    // 因为使用了weak_ptr,引用计数为1
    assert(n1.use_count() == 1);
    assert(n2.use_count() == 1);
    if (!n1->next.expired()) {  // 检查指针是否有效
        auto ptr = n1->next.lock(); // lock()获取shared_ptr
        assert(ptr == n2);
    }
}
  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-10-06 12:00:37  更:2021-10-06 12:01:27 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 1:22:12-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码