一、C11c11是c发展以来的第二个主要版本是从c98开始的最重要的更新。之前的博客接触到的都是c98最开始的版本所涉及的内容接下来会讲解C11里面用的最多也是最重要的语法。在这里插入图片描述二、列表初始化1. c98和c11里的{}C98里传统的{}一般只能用于数组和结构体的构造而到了C11它几乎能实现一切对象的初始化。{}初始化也被称为列表初始化。内置类型支持自定义类型同样支持它的本质是类型转换构造 拷贝构造中间会产生临时对象之后编译器优化变成了直接构造。{}初始化的过程中可以直接省略掉不过习惯上还是加上。对于容器的insert/push操作就会变得很方便不需要构造对象或匿名对象传递可以之间用{}初始化之后直接传递。代码语言javascriptAI代码解释struct Point { int _x; int _y; }; class Date { public: Date(int year 1, int month 1, int day 1) :_year(year) , _month(month) , _day(day) { cout Date(int year, int month, int day) endl; } Date(const Date d) :_year(d._year) , _month(d._month) , _day(d._day) { cout Date(const Date d) endl; } private: int _year; int _month; int _day; }; int main() { //c98 int a[] { 1, 2, 3, 4, 5 }; int a2[5] { 1, 2, 3, 4, 5 };//数组 Point p { 1, 2 };//结构体 //c11 int x1 { 2 }; int x2{ 2 };//可以不带等号 Date d1 { 2025, 1, 1 }; const Date d2 { 2025, 1, 2 }; //引用的是{202512}构造的临时对象 Date d3 { 2025 }; Date d4 2025; //C98支持单参数隐式转换可以不用{}这个是不能省略号的 vectorDate v; v.push_back(d1); v.push_back({ 2025, 1, 1 });//可以直接用{}初始化后传参 return 0; }2. 与C11里的std::initializer_list区分可能会存在initializer_list构造函数和{}列表初始化分不清的情况其实很好区分它们有一个本质的区别。{}列表初始化是一种语法而initializer_list是一种语义。在这里插入图片描述initializer_list构造必须要有initializer_list构造函数是根据构造函数的参数传递参数它所传递的实参跟普通构造函数的形参是一一对应的关系initializer_list构造函数只有一个形参但这个形参可以包含任意数量的值这个值要和普通构造函数一一对应。而{}列表初始化则与构造函数类型无关它能够调用任意普通构造函数优先会考虑initializer_list构造如果没有那么就会去找更加匹配的构造函数。initializer_list底层的实现是两个指针一个指向存储数据的数组一个临时数组的开始一个指向数组的结尾。在这里插入图片描述代码语言javascriptAI代码解释auto il { 1,2,3,4,5,6,7,8,8 }; cout sizeof(il) endl; cout typeid(il).name() endl;在这里插入图片描述64位系统里指针的大小是8个字节两个就是16个字节大小。很多时候我们都是initializer_list构造和{}列表初始化调用其他构造函数混合构造。比如这是因为pair类型没有initializer_list构造而STL容器都有initializer_list构造。代码语言javascriptAI代码解释mapint, int m { {1, 1}, {2, 2} }; //pair类型{}列表调用其他的构造函数初始化pair类型再通过initializer_list外面的这个{}整个构造。三、右值引用和移动语义C11增加了右值引用语法特性在这之前学到过的引用叫做左值引用。无论是左值引用还是右值引用它们的作用都是给对象起别名。3.1 左值和右值左值是一个有明确内存地址通常有变量名声明周期超出当前语句的表达式也就是有持久状态不会马上销毁。我们可以用获取它的地址定义时const修饰后的左值虽然不能给它赋值但是可以取它的地址。左值可以在赋值符号的左边也可以在右边。右值也是一个表达式但它不是一个有持久内存地址的表达式而是一个临时性的声明周期短暂的表达式通常也没有变量名与其绑定。比如字面量常量102比如表达式求值过程中产生的临时变量。右值可以出现在赋值符号的右边但不能出现在左边右值不能取地址。左值和右值最本质的区别就是能否取地址左值的英文简写为lvalue,右值的英文简写为rvalue。传统认为它们分别是leftvalue、rightvalue的缩写。在现代C中lvalue被解释为loactor value的缩写可意为存储在内存中有明确地址可以取地址的对象而rvalue被解释为 read value指的是那些可以提供数据值但是不可以寻址例如临时变量字面量常量存储于寄存器中的变量等也就是说左值和右值的核心区别就是能否取地址。代码语言javascriptAI代码解释int* p new int(0);//指针变量p cout p endl; int b 1;//普通变量b cout b endl; const int c b;//const变量c cout c endl; *p 10;//指针解引用 cout *p endl; string s(123);//string类对象s cout s endl; s[0] x;//类成员访问另类指针解引用 cout (void*) s[0] endl; //s[0]是char*类型编译器会对其特殊处理将其视为c风格字符串进行打印 //所以要强转成其他类型如void*在这里插入图片描述代码语言javascriptAI代码解释10;//字面量常量 x y;//运算产生临时变量 fmin(x, y);//函数返回值 string(111);//匿名对象在这里插入图片描述这些都是右值因为它们都无法取到地址。string(“111”)也是不行的。3.2 左值引用和右值引用左值引用只有一个右值引用则有两个为如Type l1 x, Type r1 y。第一个为左值引用也就是给左值x取别名第二个是右值引用也就是给右值y起别名。无论是左值引用还是右值引用它们的作用都是起别名。左值引用不能够直接引用右值但const左值引用可以引用右值。右值引用不能够直接引用左值得用move强转一下类似于强转为Type也就是可以引用move(左值)。move它是库里面的一个函数模板它的功能就是实现强制转换不过它还涉及一些引用折叠的知识。需要注意的是变量表达式全部都是左值属性也就意味着一个右值被右值引用绑定之后这个右值引用变量表达式的属性是左值属性。左值引用和右值引用都是取别名它们的底层也都是通过指针实现的。