《探索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修饰符后的左 值,不能给他赋值,但是可以取它的地址(可取地址的变量就是左值)

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

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

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

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

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

注意:

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

(2)(x+y)返回的是一个结果,所以为右值
**右值引用:**右值引用就是对右值的引用,给右值取别名(用两个 & 符号)

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

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

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

cpp 复制代码
int a = 0;
int b = 0;

//const左值引用左值
const int& c = a;
//const左值引用右值
const int& d = a + b;
cpp 复制代码
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,过程图如下:

cpp 复制代码
string Func()
{
	string S("abcde");

	return S;
}

int main()
{
	string V = Func();

	return 0;
}

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

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

(2)赋值运算符重载

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

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

cpp 复制代码
string operator=(const string& date)
{
	//开空间
	string tmp(date);

	//拷贝数据

	//返回
	return tmp;
}

string operator=(string&& date)
{
	//Swap交换数据
	swap(date);

	//返回
	return *this;
}
(3)移动构造

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

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

cpp 复制代码
// 拷贝构造
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<数据类型>,保持数据原本的属性(左值/右值),例如:

cpp 复制代码
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,也是有原因的,我们来看到推导的类型是特别复杂的


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

cpp 复制代码
auto F1 = [](int x, int y) {return 0; };
lambda的特殊操作
(1)只能调用静态、全局性的变量、函数
(2)引用方式捕捉

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

(3)引用捕获所有变量

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

(4)混合捕捉变量

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


相关推荐
菩提树下的凡夫18 分钟前
Python 环境管理工具
开发语言·python
索荣荣34 分钟前
JavaToken实战指南:从原理到应用
开发语言·python
zho_uzhou39 分钟前
c++ imgui implot绘图使用示例 visual studio
开发语言·c++·visual studio
dyyx11141 分钟前
C++中的过滤器模式
开发语言·c++·算法
星夜泊客1 小时前
C# 基础:为什么类可以在静态方法中创建自己的实例?
开发语言·经验分享·笔记·unity·c#·游戏引擎
CappuccinoRose1 小时前
React框架学习文档(七)
开发语言·前端·javascript·react.js·前端框架·reactjs·react router
机器视觉知识推荐、就业指导1 小时前
用惯了QTimer定时器,如何快速在纯 C++ 项目中替换?
c++
消失的旧时光-19431 小时前
从拷贝到移动:C++ 移动构造与移动赋值是怎么被逼出来的?(附完整示例)
开发语言·c++
古译汉书1 小时前
部分.exe文件打开但是一直显示界面,点击任务栏持续无反应
开发语言·单片机·嵌入式硬件
2301_817497331 小时前
C++中的装饰器模式高级应用
开发语言·c++·算法