当前位置: 首页 > news >正文

C++中的智能指针类别

目录

摘要

std::unique_ptr

std::shared_ptr

std::weak_ptr

补充:

std::make_unique

std::make_shared

其它

管理动态内存

总结


摘要

C++中的智能指针是用来管理动态内存的对象,避免我们手动管理内存时可能会带来的复杂性和错误。C++标准库提供了几种不同类型的智能指针,包括`std::unique_ptr`、`std::shared_ptr`和`std::weak_ptr`。

std::unique_ptr

1. `std::unique_ptr`是独占所有权的智能指针,即同一时间只能有一个`std::unique_ptr`指向特定的对象。
2. 当`std::unique_ptr`被销毁时,它所管理的对象会被自动释放。
3. 不能复制`std::unique_ptr`,只能移动它。
4. 当`std::unique_ptr`超出作用域时,它会自动释放资源。
5. 避免从原始指针创建多个`std::unique_ptr`,这会导致双重释放问题。

#include <iostream>
#include <memory>class MyClass {
public:MyClass() {std::cout << "MyClass Constructor" << std::endl;}~MyClass() {std::cout << "MyClass Destructor" << std::endl;}void show() {std::cout << "MyClass show" << std::endl;}
};void uniquePtrExample() {std::unique_ptr<MyClass> ptr = std::make_unique<MyClass>();ptr->show();// 移动所有权std::unique_ptr<MyClass> ptr2 = std::move(ptr);if (!ptr) {std::cout << "ptr is null after move" << std::endl;}ptr2->show();
}int main() {uniquePtrExample();return 0;
}

std::shared_ptr

1. `std::shared_ptr`是共享所有权的智能指针,即可以有多个`std::shared_ptr`指向同一个对象。
2. 当最后一个`std::shared_ptr`被销毁时,对象会被自动释放。
3. `std::shared_ptr`使用引用计数来管理对象,当引用计数为零时,释放对象。
4. 避免循环引用(Circular Reference),这会导致内存泄漏。使用`std::weak_ptr`解决这个问题。
5. 使用`std::make_shared`创建`std::shared_ptr`,这可以减少内存分配的次数并提高性能。

#include <iostream>
#include <memory>class MyClass {
public:MyClass() {std::cout << "MyClass Constructor" << std::endl;}~MyClass() {std::cout << "MyClass Destructor" << std::endl;}void show() {std::cout << "MyClass show" << std::endl;}
};void sharedPtrExample() {std::shared_ptr<MyClass> ptr1 = std::make_shared<MyClass>();{std::shared_ptr<MyClass> ptr2 = ptr1;ptr2->show();std::cout << "Reference count: " << ptr1.use_count() << std::endl;}std::cout << "Reference count after scope: " << ptr1.use_count() << std::endl;
}int main() {sharedPtrExample();return 0;
}

std::weak_ptr

1. `std::weak_ptr`是一个不拥有所有权的智能指针,它用于监视`std::shared_ptr`的生命周期。
2. `std::weak_ptr`不会影响`std::shared_ptr`的引用计数,主要用于避免循环引用。
3. `std::weak_ptr`不能直接访问对象,需要通过`lock`方法获取`std::shared_ptr`来访问对象。
4. `std::weak_ptr`用于打破`std::shared_ptr`的循环引用。

#include <iostream>
#include <memory>class MyClass {
public:MyClass() {std::cout << "MyClass Constructor" << std::endl;}~MyClass() {std::cout << "MyClass Destructor" << std::endl;}void show() {std::cout << "MyClass show" << std::endl;}
};void weakPtrExample() {std::shared_ptr<MyClass> ptr1 = std::make_shared<MyClass>();std::weak_ptr<MyClass> weakPtr = ptr1;if (auto sharedPtr = weakPtr.lock()) {sharedPtr->show();std::cout << "Reference count: " << sharedPtr.use_count() << std::endl;} else {std::cout << "The managed object has been deleted." << std::endl;}
}int main() {weakPtrExample();return 0;
}

补充:

`std::make_unique`和`std::make_shared`分别是C++14和C++11时期引入的工厂函数,用于创建智能指针对象。提供了一种安全、高效的方式来分配内存并初始化智能指针,从而减少我们手动使用`new`带来的错误风险。

std::make_unique

`std::make_unique`用于创建一个`std::unique_ptr`实例。

1. 防止内存泄漏:

使用`std::make_unique`可以防止在内存分配和对象构造之间发生异常时的内存泄漏问题。

2. 安全方面:

相比手动使用`new`,`std::make_unique`更加安全,不会产生裸指针。

#include <iostream>
#include <memory>class MyClass {
public:MyClass(int x) : value(x) {std::cout << "MyClass Constructor, value: " << value << std::endl;}~MyClass() {std::cout << "MyClass Destructor" << std::endl;}void show() {std::cout << "Value: " << value << std::endl;}
private:int value;
};void uniquePtrExample() {std::unique_ptr<MyClass> ptr = std::make_unique<MyClass>(42);ptr->show();
}int main() {uniquePtrExample();return 0;
}

std::make_shared

`std::make_shared`用于创建一个`std::shared_ptr`实例。

1. 单次内存分配:

`std::make_shared`通过一次内存分配同时创建控制块和对象,从而减少了内存分配的次数,提升了性能。

2. 防止内存泄漏:

使用`std::make_shared`可以防止在内存分配和对象构造之间发生异常时的内存泄漏问题。

3. 简洁和安全:

相比手动使用`new`,`std::make_shared`更加简洁且安全,不会产生裸指针。

#include <iostream>
#include <memory>class MyClass {
public:MyClass(int x) : value(x) {std::cout << "MyClass Constructor, value: " << value << std::endl;}~MyClass() {std::cout << "MyClass Destructor" << std::endl;}void show() {std::cout << "Value: " << value << std::endl;}
private:int value;
};void sharedPtrExample() {std::shared_ptr<MyClass> ptr = std::make_shared<MyClass>(42);ptr->show();
}int main() {sharedPtrExample();return 0;
}

其它

`std::make_shared`通过一次内存分配同时创建控制块和对象,提升了性能,但如果不需要共享所有权,可以优先选择`std::make_unique`。

管理动态内存

我们使用`std::make_shared`创建了树状结构的节点,并使用智能指针来管理节点的生命周期。通过`std::make_shared`,我们确保了节点在超出作用域时会自动释放内存,避免了内存泄漏。

#include <iostream>
#include <memory>
#include <vector>class Node {
public:Node(int value) : value(value) {std::cout << "Node Constructor, value: " << value << std::endl;}~Node() {std::cout << "Node Destructor, value: " << value << std::endl;}void addChild(std::shared_ptr<Node> child) {children.push_back(child);}void show() const {std::cout << "Node value: " << value << ", children: ";for (const auto& child : children) {std::cout << child->value << " ";}std::cout << std::endl;}
private:int value;std::vector<std::shared_ptr<Node>> children;
};void treeExample() {auto root = std::make_shared<Node>(1);auto child1 = std::make_shared<Node>(2);auto child2 = std::make_shared<Node>(3);root->addChild(child1);root->addChild(child2);root->show();child1->show();child2->show();
}int main() {treeExample();return 0;
}

总结

1. 使用`std::make_unique`和`std::make_shared`:
   优先使用`std::make_unique`和`std::make_shared`来创建智能指针,避免手动使用`new`,可以提高代码的安全性和性能。

2. 避免循环引用:
   在使用`std::shared_ptr`时要特别小心循环引用的问题。可以使用`std::weak_ptr`来打破循环引用。

3. 注意生命周期:
   确保智能指针的生命周期管理正确,避免悬空指针和内存泄漏。

4. 考虑性能:
   虽然智能指针极大地简化了内存管理,但也带来了额外的开销,如引用计数的维护。因此,在性能敏感的代码中,应慎重使用`std::shared_ptr`。

相关文章:

  • 汽车MCU虚拟化--对中断虚拟化的思考(1)
  • 利用GNSS IMU集成提高车道级定位精度
  • Blueprints - Collision Presets相关
  • 4月啤酒品类线上销售数据分析
  • Java-集合基础
  • LeetCode # 1070. 产品销售分析 III
  • Python列表
  • RustDesk服务器
  • 整理GTX收发器示例工程(高速收发器十一)
  • 医院该如何应对网络安全?
  • Redis缓存(笔记一:缓存介绍和数据库启动)
  • C. Turtle and an Incomplete Sequence
  • 一维时间序列信号的改进小波降噪方法(MATLAB R2021B)
  • 【记录43】el-table @selection-change 数据回显、条件约束、历史回显清除
  • 【AVL Design Explorer DOE】
  • 2018以太坊智能合约编程语言solidity的最佳IDEs
  • echarts花样作死的坑
  • JDK 6和JDK 7中的substring()方法
  • Js基础知识(四) - js运行原理与机制
  • MySQL数据库运维之数据恢复
  • Nacos系列:Nacos的Java SDK使用
  • vue2.0项目引入element-ui
  • Vultr 教程目录
  • 基于游标的分页接口实现
  • 将回调地狱按在地上摩擦的Promise
  • 聊聊spring cloud的LoadBalancerAutoConfiguration
  • 面试总结JavaScript篇
  • 如何选择开源的机器学习框架?
  • 数据库写操作弃用“SELECT ... FOR UPDATE”解决方案
  • 数组大概知多少
  • 我与Jetbrains的这些年
  • 小程序测试方案初探
  • 小程序开发之路(一)
  • 国内开源镜像站点
  • # AI产品经理的自我修养:既懂用户,更懂技术!
  • $NOIp2018$劝退记
  • (2)STL算法之元素计数
  • (2024)docker-compose实战 (9)部署多项目环境(LAMP+react+vue+redis+mysql+nginx)
  • (AngularJS)Angular 控制器之间通信初探
  • (poj1.2.1)1970(筛选法模拟)
  • (二)windows配置JDK环境
  • (分布式缓存)Redis持久化
  • (附源码)springboot掌上博客系统 毕业设计063131
  • (三)centos7案例实战—vmware虚拟机硬盘挂载与卸载
  • (算法)Game
  • (学习总结)STM32CubeMX HAL库 学习笔记撰写心得
  • (转) Face-Resources
  • (转) SpringBoot:使用spring-boot-devtools进行热部署以及不生效的问题解决
  • . ./ bash dash source 这五种执行shell脚本方式 区别
  • .NET CORE 2.0发布后没有 VIEWS视图页面文件
  • .NET/C# 阻止屏幕关闭,阻止系统进入睡眠状态
  • .NET/MSBuild 中的发布路径在哪里呢?如何在扩展编译的时候修改发布路径中的文件呢?
  • .NET+WPF 桌面快速启动工具 GeekDesk
  • .Net6 Api Swagger配置
  • .NET建议使用的大小写命名原则