现代C++核心特性——内存篇

"modern C++(现代 C++)"这个概念通常是相对"经典 C++"(Classic C++)而言的,主要指从 C++11 开始的一系列语言标准,它们在语法、性能、安全性、并发等方面做出了非常大的改进。是 C++ 历史上最重大的版本更新之一,被称为 "现代C++的起点"。

其主要改进可以分为以下几个方面:

本篇主要讲解内存 部分主要特性,往期博客链接:
现代C++核心特性------语法篇-CSDN博客

现代C++核心特性------类型篇-CSDN博客


1 智能指针

(std::shared_ptr, std::unique_ptr, std::weak_ptr)

在传统 C++ 中,我们通常这样动态分配内存:

cpp 复制代码
int* p = new int(5);
delete p;

如果忘记 delete,就会导致内存泄漏 ;如果多次delete,就会产生悬空指针 或程序崩溃 。为了解决这些问题,C++11 引入了智能指针类,它能在对象不再使用时自动释放内存,从而减少内存泄漏、悬空指针等错误。

智能指针本质上是一个类模板 ,它内部包装了一个普通指针,并在析构时自动调用 delete。

std::unique_ptr ------ 独占所有权

  • 同一时间只能有一个指针指向某个对象
  • 禁止拷贝,只能 移动(move,见1.1.4中移动语义)

std::shared_ptr ------ 共享所有权

  • 多个智能指针可以共享同一块堆内存
  • 内部维护一个引用计数器(reference count)
  • 当最后一个指针销毁时,才释放内存

std::weak_ptr ------ 弱引用

  • 与 shared_ptr 配合使用
  • 不增加引用计数
  • 用于打破循环引用

2 移动语义

在传统 C++ 中,复制对象时往往会造成性能浪费:

cpp 复制代码
std::vector<int> v1 = {1, 2, 3, 4, 5};
std::vector<int> v2 = v1;  // 调用拷贝构造函数,复制所有元素

如果我们知道 v1 之后不再使用,其实没必要复制,可以"搬运"资源过去,这就是 移动语义。

cpp 复制代码
std::vector<int> v3 = std::move(v1); // 调用移动构造函数

此时:

  1. 调用了 std::vector 的 移动构造函数;

  2. v3 接管 了 v1 的内部资源(例如那块存放元素的堆内存指针);

  3. v1 被"掏空"了,但 并没有被销毁 。(v1 仍然处于可用状态 :可以安全地调用它的方法、重新赋值、销毁,但它的内容(数据)已经被转移走了,通常处于空或未定义但合法的状态)

内部机制:std::move( )将一个左值显式地转换为右值引用,从而启用移动语义。

作用:解决拷贝效率低的问题,显著提升 STL 容器的性能(如 vector、string)。

在这里补充介绍一下右值引用的概念:

cpp 复制代码
//举个例子:
int a = 10;
int &&r1 = 10;   // ✅ 合法:10 是右值
// int &&r2 = a; // ❌ 错误:不能绑定到左值

万能引用(T&&)是模板推导中一种特殊的右值引用,它可以根据实参类型自动折叠为左值引用或右值引用,是实现完美转发的关键。

出现条件:

cpp 复制代码
// 1. 类型是模板参数形式的 T&&
template <typename T>
void func(T&& arg);   // ✅ 满足条件
// 2. 参数的类型由模板推导得出(不是显式指定类型)
int x = 10;
func(x);      // ✅ T 被推导
func(10);     // ✅ T 被推导
func<int>(10); // ❌ 显式指定T=int,此时是右值引用,不是万能引用

但实际编程过程中,右值很少显式使用,多用于服务移动语义(std::move ) 和**完美转发(std::forward)**功能,移动语义我们刚才已经讲过了,接下来再来看看完美转发。

有时候我们写一个包装函数(wrapper),希望它把参数原封不动地传递给另一个函数:

cpp 复制代码
void process(int& x)      { cout << "左值引用版本\n"; }
void process(int&& x)     { cout << "右值引用版本\n"; }

template<typename T>
void wrapper(T arg) {       // ❌ 错误示范
    process(arg);           // 无论传什么,arg 都是左值!
}

包装函数vs普通函数

结合模板和右值引用,实现参数的高效转递:

cpp 复制代码
template<typename T>
void wrapper(T&& arg) {
    process(std::forward<T>(arg)); // ✅ 完美转发
}
int a = 5;
wrapper(a);    // 输出:左值引用版本
wrapper(10);   // 输出:右值引用版本

内部机制:引用折叠规则 让模板可以自动区分左值和右值,从而实现完美转发。


不断学习,持续成长,欢迎大家一起交流学习!

相关推荐
WL学习笔记24 分钟前
vector模拟实现
c++
La Pulga2 小时前
【FreeRTOS源码学习笔记】任务延时列表
笔记·mcu·学习·嵌入式·freertos·实时操作系统
蜗牛一步步往上爬3 小时前
第十六章 C++语言在深度学习中的应用
开发语言·c++·深度学习
楚山之石3 小时前
【JCO】mpMRI 先行替代 TURBT 初始分期:BladderPath 随机试验让 MIBC 患者提前 45 天得到正确治疗
人工智能·学习·mri·医学影像
张张123y5 小时前
DSH 插件调试避坑:client.js 设置面板、热重载与高频报错排查表
开发语言·javascript·人工智能·开源·ecmascript·后端开发·ai agent
Peter-OK6 小时前
PostgreSQL学习笔记(入门-进阶-精通说明)
笔记·学习·postgresql
wo_jack6 小时前
C++ 入门笔记(十六):第16课 vector 模拟实现十问十答 —— 从三指针到迭代器失效
c++·后端·面试·stl·vector
承渊政道7 小时前
Linux网络学习【Reactor反应堆模式】
linux·网络·学习·ubuntu·reactor
D e re k ZH7 小时前
C++ 标准库算法全景指南
数据结构·c++·算法
冻柠檬飞冰走茶8 小时前
字母移动游戏题解:贪心求最长可保留连续子串(附 C 实现)
c语言·开发语言·数据结构·算法·动态规划