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

《探索C++11:现代C++语法的性能革新(上篇)》

前引:C++11标准标志着C++语言的一次划时代飞跃,它不仅填补了C++98/03的诸多空白,更引入了革命性特性,彻底重塑了现代软件开发的面貌。在性能与安全的双重驱动下,C++11通过智能指针(如std::unique_ptrstd::shared_ptr)终结了手动内存管理的风险,借助auto关键字和lambda表达式实现了类型推导与函数式编程的无缝融合,使C++在高性能计算和系统级开发中更具竞争力。本文将系统解析C++11的核心语法机制,揭示其如何从底层语法细节出发,引领开发者步入更安全、更高效的编程新时代!

目录

【一】左值和右值

【一】何为左值与右值

【二】左值引用与右值引用相关特性

左值引用

右值引用

【三】右值引用的效率提升

(1)传值返回

(2)赋值运算符重载

(3)移动构造

【四】右值引用的隐藏精髓

(1)自动识别为左值

(2)forward完美转发

(3)万能引用

【二】decltype类型推导

【三】nullptr与null

【四】lambda智能排序

lambda的特殊操作

(1)只能调用静态、全局性的变量、函数

(2)引用方式捕捉

(3)引用捕获所有变量

(4)混合捕捉变量


【一】左值和右值

【一】何为左值与右值

左值:左值是一个表示数据的表达式(如变量名或解引用的指针),我们可以获取它的地址+可以对它赋值,左值可以出现赋值符号的左边,右值不能出现在赋值符号左边。定义时const修饰符后的左 值,不能给他赋值,但是可以取它的地址(可取地址的变量就是左值)

// 以下的p、b、c、*p都是左值
int* p = new int(0);
int b = 1;
const int c = 2;

左值引用:左值引用就是给左值的引用,给左值取别名(用一个 & 符号)

// 以下几个是对上面左值的左值引用
int*& rp = p;
int& rb = b;
const int& rc = c;
int& pvalue = *p;

右值:右值也是一个表示数据的表达式,通常为纯右值和将亡值,如:字面常量、表达式返回值,函数返回值(这个不能是左值引用返回)等等,右值可以出现在赋值符号的右边,但是不能出现出现在赋值符号的左边,右值不能取地址(不能取地址的就是右值)

// 以下几个都是常见的右值
10;
x + y;
fmin(x, y);

注意:

(1)常量字符串“XXXXXXXXX”之所以为左值,是因为这个表达式返回的是首元素的地址

(2)(x+y)返回的是一个结果,所以为右值

右值引用:右值引用就是对右值的引用,给右值取别名(用两个 & 符号)

// 以下几个都是对右值的右值引用
int&& rr1 = 10;
double&& rr2 = x + y;
double&& rr3 = fmin(x, y);
【二】左值引用与右值引用相关特性
左值引用

(1)左值引用只能引用左值,不能引用右值

(2)const 左值引用可以引用/接收左值和右值,例如:

int a = 0;
int b = 0;//const左值引用左值
const int& c = a;
//const左值引用右值
const int& d = a + b;

void Func(const int& date)
{cout << "const左值引用" << endl;
}int main()
{int a = 0;int b = 0;//const左值引用可以接收左值 或者 右值Func(a);Func(a + b);return 0;}

(3)左值引用和右值引用作为函数接受参数可以进行区分(二者构成函数重载),例如:

注意:用const 左值是因为const左值既可以接收左值也可以接受右值,所以是保险做法

右值引用

(1)右值引用只能引用右值,不能引用左值,例如:

move

左值可以经过move函数转化之后被右值引用,例如:C++中的std::move是一个标准库函数,用于将对象转换为右值引用。它不实际移动任何数据,仅通过强制类型转换static_cast)将左值标记为可移动的右值,从而允许调用移动构造函数或移动赋值运算符

例如没有接接收move的返回值时不会改变原来左值的数据性质:

例如接收move的返回值之后,原左值数据性质就会改变(转移数据):

【三】右值引用的效率提升

右值的生命周期一般是很短暂的(将亡值),例如一个表达式、函数返回值。那么在C++11中将这个特性利用了起来,右值引用精髓:利用swap夺舍对方,下面我们来看几个典型的改进案例!

(1)传值返回

这是一个很常见的场景:函数内有一个临时对象S,将S传值拷贝给V,过程图如下:

string Func()
{string S("abcde");return S;
}int main()
{string V = Func();return 0;
}

后来C++11出来提出了右值/右值引用,编译器就自动优化为直接进行移动拷贝:

例如:二者地址是一样的,直接将S的内容全部转交给V,S变量出了函数就销毁了

(2)赋值运算符重载

深拷贝:开空间->拷贝数据

移动拷贝:直接swap交换数据

string operator=(const string& date)
{//开空间string tmp(date);//拷贝数据//返回return tmp;
}string operator=(string&& date)
{//Swap交换数据swap(date);//返回return *this;
}
(3)移动构造

我们知道左值引用和右值引用是构成函数重载的,因此如果可以利用将亡值,那就避免了拷贝构造

精髓:利用将亡值直接交换给 *this ,避免了再次开辟空间

// 拷贝构造
Func(const string& s):_str(nullptr)
{cout << "string(const string& s) -- 深拷贝" << endl;string tmp(s._str);swap(tmp);
}// 移动构造
Func(string&& s):_str(nullptr), _size(0), _capacity(0)
{cout << "string(string&& s) -- 移动语义" << endl;swap(s);
}
【四】右值引用的隐藏精髓
(1)自动识别为左值

右值应该是不能修改和取地址的,下面我们看两个例子:

通过实操我们可以看出,对代表右值10的变量pc取引用和修改,是可以的,因此:

右值引用的变量会被编译器识别为左值,否则在移动构造的场景下,无法完成“夺舍”和数据修改

(2)forward完美转发

右值引用的变量会被强制识别为左值,那么对于多层函数的调用来说就失去了右值引用带来的效率

因此C++11推出了完美转发forward<数据类型>,保持数据原本的属性(左值/右值),例如:

Func(forward<int>(pc));

我们来看看效果:

(3)万能引用

万能引用就是根据参数的类型(左值/右值)自动变换,需要使用模板完成

【二】decltype类型推导

学习 decltype 之前我们需要先看两个语法:

auto:自动推导类型,用来定义变量,且变量必须初始化

typeid( ).name( ):获得变量类型,只可获得不能使用

decltype:可以根据变量\表达式推导类型+使用,且可以不初始化(比如模板参数)

【三】nullptr与null

在C中,NULL其实是#define定义的整型0,而指针可被初始化为NULL,是发生了类型转化

在C++中,为了清晰和安全的考虑,更新出了 nullptr,专门用来表示空指针

例如:

【四】lambda智能排序

现在有仿函数排序如图:

而学了lambda智能排序可以这么写(必须使用auto):

[ ](数据类型date1,数据类型date2)->bool { return 操作 ;}

注意:这里的操作就是函数内部的实现,可以写交换、赋值.......

解释:这里必须使用auto,也是有原因的,我们来看到推导的类型是特别复杂的

而在日常中,返回值类型和箭头可以不写,编译器自己推导,例如:

auto F1 = [](int x, int y) {return 0; };
lambda的特殊操作
(1)只能调用静态、全局性的变量、函数

(2)引用方式捕捉

在捕捉列表里面我们可以使用引用的方式捕捉(后可不在参数列表书写),否则是对变量的拷贝

(3)引用捕获所有变量

当捕捉列表只有一个取地址符号时,会引用收集它之前的所有变量,当然全局的本身就可以调用

(4)混合捕捉变量

混合指的是:引用捕捉+传值捕捉。此时除了 y 其它属于引用捕捉,y在函数里面是拷贝属于右值

http://www.dtcms.com/a/360728.html

相关文章:

  • 医疗AI时代的生物医学Go编程:高性能计算与精准医疗的案例分析(八)
  • Redis 核心概念解析:从渐进式遍历、数据库管理到客户端通信协议
  • 《C++进阶之STL》【红黑树】
  • C语言数据结构之双向链表
  • 基于 DNA 的原核生物与微小真核生物分类学:分子革命下的范式重构​
  • 【JavaWeb】之HTML(对HTML细节的一些总结)
  • Notepad++近期版本避雷
  • 【golang长途旅行第35站】Redis
  • Objective-C 的坚毅与传承:在Swift时代下的不可替代性优雅草卓伊凡
  • 云市场周报 (2025.09.01):解读腾讯云向量数据库、阿里云西安节点与平台工程
  • 从零开始的云计算生活——第五十五天,黑云压城,kubernetes模块之网络组件和CoreDNS组件
  • 数组(3)
  • Proteus8 仿真教学全指南:从入门到实战的电子开发利器
  • GitHub 热榜项目 - 日榜(2025-09-01)
  • 基于YOLOv11的脑卒中目标检测及其完整数据集——推动智能医疗发展的新机遇!
  • MySQL下载及安装(Windows 11)
  • 【LeetCode】3524. 求出数组的 X 值 I (动态规划)
  • 【LeetCode 155】—最小栈 - 详解与实现
  • 阿里Qoder怎么样?实测对比TRAE SOLO 和 CodeBuddy IDE
  • 保健品跨境电商:如何筑牢产品质量与安全防线?
  • 数据库事务隔离级别与 MVCC 机制详解
  • 机器学习(四)KNN算法-分类
  • 哈希表-1.两数之和-力扣(LeetCode)
  • git将当前分支推送到远端指定分支
  • YOLO 目标检测:YOLOv3网络结构、特征输出、FPN、多尺度预测
  • Redis--Lua脚本以及在SpringBoot中的使用
  • 三、Gitee平台使用指南
  • 第 94 场周赛:叶子相似的树、模拟行走机器人、爱吃香蕉的珂珂、最长的斐波那契子序列的长度
  • Eclipse Compiler for Java (ECJ):安装指南与高效快捷键全解析
  • 构建无广告私人图书馆Reader与cpolar让电子书库随身携带