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

南山商城网站建设哪家便宜华诚博远建筑规划设计公司

南山商城网站建设哪家便宜,华诚博远建筑规划设计公司,微信android平板版,一个具体网站的seo优化方案c11 列表初始化c98的{}c11的{}std::initializer_list 右值引用和移动语义左值和右值的概念左值引用和右值引用引用延长临时对象生命周期左值和右值的参数匹配左值引用的主要使用场景移动构造和移动赋值引用折叠完美转发 列表初始化 c98的{} 在C98中#xff0c;大括号 {} 的使… c11 列表初始化c98的{}c11的{}std::initializer_list 右值引用和移动语义左值和右值的概念左值引用和右值引用引用延长临时对象生命周期左值和右值的参数匹配左值引用的主要使用场景移动构造和移动赋值引用折叠完美转发 列表初始化 c98的{} 在C98中大括号 {} 的使用主要有以下几种场景 代码块 大括号用于定义一个代码块通常在控制结构如 if、for、while 等或函数定义中使用。 if (condition) {// 代码块doSomething(); }函数定义 函数体由大括号括起来。 void myFunction() {// 函数体 }类和结构体定义 类和结构体的成员变量和成员函数也由大括号括起来。 class MyClass { public:int x;void myMethod() {// 方法体} };初始化列表C98 限制) 在C98中可以使用大括号进行数组或结构体的初始化。 int arr[5] {1, 2, 3, 4, 5};struct Point {int x;int y; };Point p {10, 20};范围Scope管理 大括号可以创建新的作用域这对于管理变量的生命周期很有用。 {int temp 5;// temp 在这个作用域内有效 } // temp 在这里不可见C98 对 {} 的局限 C98 对大括号的支持相对有限没有像 C11 引入的统一初始化Uniform Initialization那样的功能。 c11的{} 在 C11 中大括号 {} 的使用引入了一些重要的变化和新特性尤其是在初始化和类型安全方面。以下是主要变化 统一初始化Uniform Initialization C11 引入了统一初始化语法可以使用大括号来初始化各种类型的变量包括基本类型、结构体、类和数组。这种语法帮助避免了一些常见的错误。 int x{5}; // 初始化基本类型 std::vectorint vec{1, 2, 3}; // 初始化容器 struct Point { int x, y; }; Point p{10, 20}; // 初始化结构体列表初始化List Initialization 使用大括号进行列表初始化可以避免窄化转换narrowing conversion。例如 // 这将会导致编译错误避免窄化转换 int x{2.5}; // 错误不能从 double 转换为 int初始化数组 在 C11 中数组的初始化也可以使用统一初始化语法 int arr[]{1, 2, 3, 4, 5}; // C11 引入的数组初始化nullptr 虽然 nullptr 不是直接与 {} 相关但它与统一初始化一起使用时可以提供更好的类型安全 int* ptr{nullptr}; // 安全的空指针初始化lambda 表达式 在 C11 中引入了 lambda 表达式虽然大括号在这里用作 lambda 的函数体但这也是一种新的使用方式。 auto func []() {std::cout Hello, World! std::endl; }; // 大括号定义 lambda 的主体std::initializer_list C11 引入了 std::initializer_list允许使用大括号初始化自定义类型方便处理多个值的初始化。 class MyClass { public:MyClass(std::initializer_listint list) {for (auto item : list) {std::cout item ;}} };// 使用大括号初始化 MyClass obj{1, 2, 3, 4};案例 #includeiostream #includevector using namespace std; 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 a1[] { 1, 2, 3, 4, 5 };int a2[5] { 0 };Point p { 1, 2 };// C11⽀持的// 内置类型⽀持int x1 { 2 };// ⾃定义类型⽀持// 这⾥本质是⽤{ 2025, 1, 1}构造⼀个Date临时对象// 临时对象再去拷⻉构造d1编译器优化后合⼆为⼀变成{ 2025, 1, 1}直接构造初始化d1// 运⾏⼀下我们可以验证上⾯的理论发现是没调⽤拷⻉构造的Date d1 { 2025, 1, 1};// 这⾥d2引⽤的是{ 2024, 7, 25 }构造的临时对象const Date d2 { 2024, 7, 25 };// 需要注意的是C98⽀持单参数时类型转换也可以不⽤{}Date d3 { 2025};Date d4 2025;// 可以省略掉Point p1 { 1, 2 };int x2 { 2 };Date d6 { 2024, 7, 25 };const Date d7 { 2024, 7, 25 };// 不⽀持只有{}初始化才能省略// Date d8 2025;vectorDate v;v.push_back(d1);v.push_back(Date(2025, 1, 1));// ⽐起有名对象和匿名对象传参这⾥{}更有性价⽐v.push_back({ 2025, 1, 1 });return 0; } std::initializer_list std::initializer_list 是 C11 中引入的一个非常有用的特性允许你使用大括号 {} 进行初始化特别适用于需要接收多个元素的构造函数、函数参数或其他需要集合的情况。 基本概念 std::initializer_list 是一个轻量级的类模板提供了一个可迭代的常量数组可以用来接收初始化的元素。 用法示例 构造函数 你可以在类的构造函数中使用 std::initializer_list 来接收多个值。 #include iostream #include initializer_list #include vectorclass MyVector { public:MyVector(std::initializer_listint list) {for (auto value : list) {vec.push_back(value);}}void print() const {for (const auto value : vec) {std::cout value ;}std::cout std::endl;}private:std::vectorint vec; };int main() {MyVector mv{1, 2, 3, 4, 5}; // 使用 initializer_listmv.print(); // 输出: 1 2 3 4 5return 0; }函数参数 std::initializer_list 也可以用作函数的参数让函数能够接收多个输入。 void printNumbers(std::initializer_listint numbers) {for (auto number : numbers) {std::cout number ;}std::cout std::endl; }int main() {printNumbers({10, 20, 30}); // 使用 initializer_listreturn 0; }注意事项 std::initializer_list 是常量类型因此你不能修改它的内容。 由于它是一个轻量级的类可以在需要的地方使用避免了额外的内存分配。 右值引用和移动语义 左值和右值的概念 左值和右值是 C 中的重要概念用于区分表达式的值类型。它们在理解资源管理和对象生命周期时尤为关键。 左值 (Lvalue) 定义左值是指可以被赋值的对象可以出现在赋值操作符的左侧。左值有持久的存储地址可以在程序中多次访问。特点 有名字并且在内存中有一个具体的位置。 可以被修改例如变量。示例 int a 10; // a 是一个左值 a 20; // 可以将值赋给左值 a右值 (Rvalue) 定义右值是指不具名的临时对象通常是表达式的结果不能出现在赋值操作符的左侧。右值通常没有持久的存储地址。特点 通常是临时的、不可修改的值。 可以是字面量、运算结果或函数返回值等。示例 int a 10; // 10 是一个右值 a a 5; // a 5 也是一个右值结合与应用 在 C11 中引入了右值引用使得可以更有效地处理临时对象优化移动语义从而提高性能。简单来说左值就是可以取地址的而右值不可以。 案例 #includeiostream using namespace std; int main() {// 左值可以取地址// 以下的p、b、c、*p、s、s[0]就是常⻅的左值int* p new int(0);int b 1;const int c b;*p 10;string s(111111);s[0] x;cout c endl;cout (void*)s[0] endl;// 右值不能取地址double x 1.1, y 2.2;// 以下⼏个10、x y、fmin(x, y)、string(11111)都是常⻅的右值10;x y;fmin(x, y);string(11111);//cout 10 endl;//cout (xy) endl;//cout (fmin(x, y)) endl;//cout string(11111) endl;return 0; }左值引用和右值引用 Type r1 x; Type rr1 y; 第⼀个语句就是左值引⽤左值引⽤就是给左值取别名第⼆个就是右值引⽤同样的道理右值引⽤就是给右值取别名。 左值引⽤不能直接引⽤右值但是const左值引⽤可以引⽤右值 右值引⽤不能直接引⽤左值但是右值引⽤可以引⽤move(左值) template typename remove_reference::type move (Targ); move是库⾥⾯的⼀个函数模板本质内部是进⾏强制类型转换还涉及⼀些引⽤折叠的知识。 需要注意的是变量表达式都是左值属性也就意味着⼀个右值被右值引⽤绑定后右值引⽤变量变量表达式的属性是左值。 下面来解释下这句话。 1, 变量表达式都是左值 这意味着所有的变量如 int a 10; 中的 a都是左值因为它们有一个持久的存储位置可以被赋值和修改。 2,右值被右值引用绑定 右值引用是 C11 引入的特性表示可以绑定到右值的引用通常用 表示。比如 int r 10; // 10 是一个右值在这里r 是一个右值引用它可以绑定到临时值 10。 3, 右值引用变量的属性是左值 一旦一个右值被绑定到右值引用虽然这个引用最初绑定的是一个右值但 r 现在本身是一个可以在赋值中使用的对象。在这种情况下右值引用 r 的性质变为左值因为它现在有一个具体的名称和存储位置。 具体示例 int r 10; // r 绑定到右值 10 int a std::move(r); // std::move(r) 是一个右值但 r 是左值在上面的例子中std::move(r ) 使得 r 的值能够被移动即转移资源但 r 本身是一个左值。 template class _Ty remove_reference_t_Ty move(_Ty _Arg) { // forward _Arg as movablereturn static_castremove_reference_t_Ty(_Arg); }#includeiostream using namespace std; int main() {// 左值可以取地址// 以下的p、b、c、*p、s、s[0]就是常⻅的左值int* p new int(0);int b 1;const int c b;*p 10;string s(111111);s[0] x;double x 1.1, y 2.2;// 左值引⽤给左值取别名int r1 b;int* r2 p;int r3 *p;string r4 s;char r5 s[0];// 右值引⽤给右值取别名int rr1 10;double rr2 x y;double rr3 fmin(x, y);string rr4 string(11111);// 左值引⽤不能直接引⽤右值但是const左值引⽤可以引⽤右值const int rx1 10;const double rx2 x y;const double rx3 fmin(x, y);const string rx4 string(11111);// 右值引⽤不能直接引⽤左值但是右值引⽤可以引⽤move(左值)int rrx1 move(b);int* rrx2 move(p);int rrx3 move(*p);string rrx4 move(s);string rrx5 (string)s;// b、r1、rr1都是变量表达式都是左值cout b endl;cout r1 endl;cout rr1 endl;// 这⾥要注意的是rr1的属性是左值所以不能再被右值引⽤绑定除⾮move⼀下int r6 r1;// int rrx6 rr1;int rrx6 move(rr1);return 0; }引用延长临时对象生命周期 基本概念 在 C 中当你创建一个临时对象通常是一个右值其生命周期通常非常短暂。在表达式结束后临时对象会被销毁。但当这个临时对象被一个常量引用const T或者右值引用绑定时它的生命周期会被延长直到该引用的作用域结束。 示例 const std::string str Hello, World!; // Hello, World! 是一个临时对象 std::cout str; // 在这里可以使用 str因为临时对象的生命周期被延长在这个例子中字符串字面量 “Hello, World!” 是一个临时对象正常情况下会在语句结束后销毁。然而由于它被一个 const std::string 引用绑定临时对象的生命周期被延长直到 str 的作用域结束例如在函数返回时。 适用场景 引用延长生命周期主要用于以下几种情况 返回值优化当函数返回一个临时对象时可以通过返回一个常量引用来延长其生命周期。 避免不必要的复制使用常量引用可以避免对临时对象的拷贝提高性能。 注意事项 不可修改使用常量引用只能读取临时对象的值不能修改。 作用域管理引用的作用域决定了临时对象的有效性如果引用超出其作用域临时对象会被销毁使用引用将导致未定义行为。 左值和右值的参数匹配 在 C 中函数可以接受不同类型的参数包括左值引用和右值引用。以下是几种常见的参数类型及其匹配规则 左值引用T只能绑定到左值。右值引用T只能绑定到右值。常量左值引用const T可以绑定到左值和右值因为常量左值引用允许对临时对象的绑定。常量右值引用const T理论上不常用因为右值引用本身就是临时对象通常不需要常量修饰。 案例 #include iostream #include utility // for std::movevoid process(int x) {std::cout Lvalue reference: x std::endl; }void process(int x) {std::cout Rvalue reference: x std::endl; }void process(const int x) {std::cout Const lvalue reference: x std::endl; }int main() {int a 10;process(a); // 绑定到左值调用 int 版本process(20); // 绑定到右值调用 int 版本process(std::move(a)); // 绑定到右值调用 int 版本process(30); // 绑定到右值调用 int 版本process(static_castconst int(20)); // 绑定到常量左值引用return 0; }选择规则 在函数重载时C 使用以下规则来确定哪个版本的函数将被调用 精确匹配如果有左值和右值的重载C 会优先选择精确匹配的版本。 const 修饰符如果没有左值引用匹配const 左值引用会被考虑。 临时对象对于右值右值引用会优先匹配。 左值引用的主要使用场景 左值引用在 C 中有许多重要的应用场景 修改已有对象左值引用可以用来修改函数外部的变量。例如当你希望在函数中改变传入的参数值时可以使用左值引用。 void increment(int x) {x; }int main() {int a 5;increment(a); // a 现在是 6 }作为返回值函数可以返回左值引用从而允许链式调用。 int getElement(std::vectorint vec, size_t index) {return vec[index]; }避免复制通过使用左值引用可以避免对象的复制提高性能特别是在处理大对象时。 缺点 1左值引用只能绑定到具有持久性地址的对象不能绑定到临时对象。这意味着无法直接修改临时值或表达式的结果。 2左值引用可以被赋值但不能用于移动语义。这意味着使用左值引用的函数不能轻易地转移资源的所有权从而可能导致不必要的资源开销。 移动构造和移动赋值 移动构造Move Constructor 概念 移动构造函数是一种特殊的构造函数用于通过移动一个对象的资源来初始化另一个对象而不是复制资源。这种方式适用于临时对象或即将被销毁的对象。 语法 移动构造函数的定义通常采用以下形式 ClassName(ClassName other) noexcept {// 转移资源this-data other.data;other.data nullptr; // 使原对象的指针失效 }示例 class MyString { public:MyString(const char* str) {data new char[strlen(str) 1];strcpy(data, str);}// 移动构造函数MyString(MyString other) noexcept {data other.data;other.data nullptr; // 使其他对象的指针失效}~MyString() {delete[] data;}private:char* data; };移动赋值Move Assignment Operator 概念 移动赋值运算符是一种特殊的赋值运算符用于将一个对象的资源转移到另一个对象而不是复制资源。这在赋值操作中非常有用尤其是在处理临时对象时。 语法 移动赋值运算符的定义通常采用以下形式 ClassName operator(ClassName other) noexcept {if (this ! other) { // 防止自我赋值delete[] data; // 释放当前资源data other.data; // 转移资源other.data nullptr; // 使原对象的指针失效}return *this; }示例 class MyString { public:MyString(const char* str) {data new char[strlen(str) 1];strcpy(data, str);}// 移动赋值运算符MyString operator(MyString other) noexcept {if (this ! other) {delete[] data; // 释放当前资源data other.data; // 转移资源other.data nullptr; // 使其他对象的指针失效}return *this;}~MyString() {delete[] data;}private:char* data; };意义 性能提升通过移动构造和移动赋值C 可以避免不必要的深拷贝显著提升性能尤其是在处理大对象或复杂数据结构时。 资源管理移动语义提供了一种简洁且高效的方式来管理动态资源降低内存分配和释放的频率。 支持临时对象移动构造和移动赋值使得临时对象的使用变得更加高效避免了临时对象的拷贝。 引用折叠 有人可能会想到能不能套娃引用的引用确实可以不过直接这样写是报错的需要通过取巧的方式这种情况就会引发引用折叠。 通过typedef的方式或者模板才能引用叠加不过这种情况下叠加的引用算什么引用呢 引用叠加的情况 通过模板或typedef中的类型操作可以构成引⽤的引⽤时这C11给出了⼀个引⽤折叠的规则右值引⽤的右值引⽤折叠成右值引⽤所有其他组合均折叠成左值引⽤。 下⾯的程序中很好的展⽰了模板和typedef时构成引⽤的引⽤时的引⽤折叠规则。 像f2这样的函数模板中T x参数看起来是右值引⽤参数但是由于引⽤折叠的规则他传递左值时就是左值引⽤传递右值时就是右值引⽤有些地⽅也把这种函数模板的参数叫做万能引⽤。 Function(Tt)函数模板程序中假设实参是int右值模板参数T的推导int实参是int左值模板参数T的推导int再结合引⽤折叠规则就实现了实参是左值实例化出左值引⽤版本形参Function实参是右值实例化出右值引⽤版本形参的Function。 // 由于引⽤折叠限定f1实例化以后总是⼀个左值引⽤ templateclass T void f1(T x) {} // 由于引⽤折叠限定f2实例化后可以是左值引⽤也可以是右值引⽤ templateclass T void f2(T x) {} int main() {typedef int lref;typedef int rref;int n 0;lref r1 n; // r1 的类型是 intlref r2 n; // r2 的类型是 intrref r3 n; // r3 的类型是 intrref r4 1; // r4 的类型是 int// 没有折叠-实例化为void f1(int x)f1int(n);f1int(0); // 报错// 折叠-实例化为void f1(int x)f1int(n);f1int(0); // 报错// 折叠-实例化为void f1(int x)f1int(n);f1int(0); // 报错// 折叠-实例化为void f1(const int x)f1const int(n);f1const int(0);// 折叠-实例化为void f1(const int x)f1const int(n);f1const int(0);// 没有折叠-实例化为void f2(int x)f2int(n); // 报错f2int(0);// 折叠-实例化为void f2(int x)f2int(n);f2int(0); // 报错// 折叠-实例化为void f2(int x)f2int(n); // 报错f2int(0);return 0; }templateclass T void Function(T t) {int a 0;T x a;//x;cout a endl;cout x endl endl; } int main() {// 10是右值推导出T为int模板实例化为void Function(intt)Function(10); // 右值int a;// a是左值推导出T为int引⽤折叠模板实例化为void Function(int t)Function(a); // 左值// std::move(a)是右值推导出T为int模板实例化为void Function(int t)Function(std::move(a)); // 右值const int b 8;// a是左值推导出T为const int引⽤折叠模板实例化为void Function(const intt)// 所以Function内部会编译报错x不能Function(b); // const 左值// std::move(b)右值推导出T为const int模板实例化为void Function(const int t)// 所以Function内部会编译报错x不能Function(std::move(b)); // const 右值return 0; }完美转发 Function(Tt)函数模板程序中传左值实例化以后是左值引⽤的Function函数传右值实例化以后是右值引⽤的Function函数。 变量表达式都是左值属性也就意味着⼀个右值被右值绑定 后右值引⽤变量表达式的属性是左值也就是说Function函数中t的属性是左值那么我们把t传递给下⼀层函数Fun那么匹配的都是左值引⽤版本的Fun函数。这⾥我们想要保持t对象的属性就需要使⽤完美转发实现。 完美转发forward本质是⼀个函数模板他主要还是通过引⽤折叠的⽅式实现下⾯⽰例中传递给Function的实参是右值T被推导为int没有折叠forward内部t被强转为右值引⽤返回传递Function的实参是左值T被推导为int引⽤折叠为左值引⽤forward内部t被强转为左值引⽤返回。 template class _Ty _Ty forward(remove_reference_t_Ty _Arg) noexcept { // forward an lvalue as either an lvalue or an rvaluereturn static_cast_Ty(_Arg); } void Fun(int x) { cout 左值引⽤ endl; }void Fun(const int x) { cout const 左值引⽤ endl; }void Fun(int x) { cout 右值引⽤ endl; }void Fun(const int x) { cout const 右值引⽤ endl; }templateclass T void Function(T t) {Fun(t);//Fun(forwardT(t)); } int main() {// 10是右值推导出T为int模板实例化为void Function(int t)Function(10); // 右值int a;// a是左值推导出T为int引⽤折叠模板实例化为void Function(int t)Function(a); // 左值// std::move(a)是右值推导出T为int模板实例化为void Function(int t)Function(std::move(a)); // 右值const int b 8;// a是左值推导出T为const int引⽤折叠模板实例化为void Function(const int t)Function(b); // const 左值// std::move(b)右值推导出T为const int模板实例化为void Function(const int t)Function(std::move(b)); // const 右值return 0; }
http://www.w-s-a.com/news/703/

相关文章:

  • 做带会员后台的网站用什么软件旅游网站建设资金请示
  • 商品网站怎么做wordpress 表情拉长
  • 商城网站设计费用网络公司怎样推广网站
  • 视频公司的网站设计工图网
  • 免费快速网站十八个免费的舆情网站