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

假网站备案公司黄页查询

假网站备案,公司黄页查询,中铁中基建设集团网站,有名的wordpress主题商http://www.cnblogs.com/yanqi0124/p/4723698.html 在C98中有左值和右值的概念#xff0c;不过这两个概念对于很多程序员并不关心#xff0c;因为不知道这两个概念照样可以写出好程序。在C11中对右值的概念进行了增强#xff0c;我个人理解这部分内容是C11引入的特性中最难以…http://www.cnblogs.com/yanqi0124/p/4723698.html 在C98中有左值和右值的概念不过这两个概念对于很多程序员并不关心因为不知道这两个概念照样可以写出好程序。在C11中对右值的概念进行了增强我个人理解这部分内容是C11引入的特性中最难以理解的了。该特性的引入至少可以解决C98中的移动语义和完美转发问题若你还不清楚这两个问题是什么请向下看。 温馨提示由于内容比较难懂请仔细看。C已经够复杂了C11中引入的新特性令C更加复杂了。在学习本文的时候一定要理解清楚左值、右值、左值引用和右值引用。 移动构造函数 首先看一个C98中的关于函数返回类对象的例子。 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566 class MyString {public: MyString() { _data nullptr; _len 0; printf(Constructor is called!\n);} MyString(const char* p) { _len strlen (p); _init_data(p); cout Constructor is called! this-_data: (long)_data endl;} MyString(const MyString str) { _len str._len; _init_data(str._data); cout Copy Constructor is called! src: (long)str._data dst: (long)_data endl;} ~MyString() { if (_data){ cout DeConstructor is called! this-_data: (long)_data endl; free(_data);} else{ std::cout DeConstructor is called! std::endl; }} MyString operator(const MyString str) { if (this ! str) { _len str._len; _init_data(str._data); } cout Copy Assignment is called! src: (long)str._data dst (long)_data endl; return *this; } operator const char *() const { return _data;}private: char *_data; size_t _len; void _init_data(const char *s) { _data new char[_len1]; memcpy(_data, s, _len); _data[_len] \0; } }; MyString foo(){ MyString middle(123); return middle;}int main() { MyString a foo(); return 1;} 该例子在编译器没有进行优化的情况下会输出以下内容我在输出的内容中做了注释处理如果连这个例子的输出都看不懂建议再看一下C的语法了。我这里使用的编译器命令为g test.cpp -o main -g -fno-elide-constructors之所以要加上-fno-elide-constructors选项时因为g编译器默认情况下会对函数返回类对象的情况作返回值优化处理这不是我们讨论的重点。 123456 Constructor is called! this-_data: 29483024 // middle对象的构造函数Copy Constructor is called! src: 29483024 dst: 29483056 // 临时对象的构造通过middle对象调用复制构造函数DeConstructor is called! this-_data: 29483024 // middle对象的析构Copy Constructor is called! src: 29483056 dst: 29483024 // a对象构造通过临时对象调用复制构造函数DeConstructor is called! this-_data: 29483056 // 临时对象析构DeConstructor is called! this-_data: 29483024 // a对象析构 在上述例子中临时对象的构造、复制和析构操作所带来的效率影响一直是C中为人诟病的问题临时对象的构造和析构操作均对堆上的内存进行操作而如果_data的内存过大势必会非常影响效率。从程序员的角度而言该临时对象是透明的。而这一问题正是C11中需要解决的问题。 在C11中解决该问题的思路为引入了移动构造函数移动构造函数的定义如下。 123456 MyString(MyString str) { cout Move Constructor is called! src: (long)str._data endl;_len str._len;_data str._data;str._data nullptr;} 在移动构造函数中我们窃取了str对象已经申请的内存将其拿为己用并将str申请的内存给赋值为nullptr。移动构造函数和复制构造函数的不同之处在于移动构造函数的参数使用这就是下文要讲解的右值引用符号。参数不再是const因为在移动构造函数需要修改右值str的内容。 移动构造函数的调用时机为用来构造临时变量和用临时变量来构造对象的时候移动语义会被调用。可以通过下面的输出结果看到我们所使用的编译参数为g test.cpp -o main -g -fno-elide-constructors --stdc11。 123456 Constructor is called! this-_data: 22872080 // middle对象构造Move Constructor is called! src: 22872080 // 临时对象通过移动构造函数构造将middle申请的内存窃取DeConstructor is called! // middle对象析构Move Constructor is called! src: 22872080 // 对象a通过移动构造函数构造将临时对象的内存窃取DeConstructor is called! // 临时对象析构DeConstructor is called! this-_data: 22872080 // 对象a析构 通过输出结果可以看出整个过程中仅申请了一块内存这也正好符合我们的要求了。 C98中的左值和右值 我们先来看下C98中的左值和右值的概念。左值和右值最直观的理解就是一条语句等号左边的为左值等号右边的为右值而事实上该种理解是错误的。左值可以取地址有名字的值是一个指向某内存空间的表达式可以使用操作符获取内存地址。右值不能取地址即非左值的都是右值没有名字的值是一个临时值表达式结束后右值就没有意义了。我想通过下面的例子读者可以清楚的理解左值和右值了。 12345678910111213141516 // lvalues://int i 42;i 43; // i是左值int* p i; // i是左值int foo();foo() 42; // foo()返回引用类型是左值int* p1 foo(); // foo()可以取地址是左值// rvalues://int foobar();int j 0;j foobar(); // foobar()是右值int* p2 foobar(); // 编译错误foobar()是右值不能取地址j 42; // 42是右值 C11右值引用和移动语义 在C98中有引用的概念对于const int m 1其中m为引用类型可以对其取地址故为左值。在C11中引入了右值引用的概念使用来表示。在引入了右值引用后在函数重载时可以根据是左值引用还是右值引用来区分。 12345678910111213141516 void fun(MyString str){ cout left reference endl;}void fun(MyString str){ cout right reference endl;}int main() { MyString a(456); fun(a); // 左值引用调用void fun(MyString str)fun(foo()); // 右值引用调用void fun(MyString str) return 1;} 在绝大多数情况下这种通过左值引用和右值引用重载函数的方式仅会在类的构造函数和赋值操作符中出现被例子仅是为了方便采用函数的形式该种形式的函数用到的比较少。上述代码中所使用的将资源从一个对象到另外一个对象之间的转移就是移动语义。这里提到的资源是指类中的在堆上申请的内存、文件描述符等资源。 前面已经介绍过了移动构造函数的具体形式和使用情况这里对移动赋值操作符的定义再说明一下并将main函数的内容也一起更改将得到如下输出结果。 1234567891011121314151617181920212223242526272829 MyString operator(MyString str) { cout Move Operator is called! src: (long)str._data endl; if (this ! str) { if (_data ! nullptr){ free(_data);}_len str._len;_data str._data;str._len 0;str._data nullptr;} return *this; }int main() { MyString b;b foo(); return 1;}// 输出结果整个过程仅申请了一个内存地址Constructor is called! // 对象b构造函数调用Constructor is called! this-_data: 14835728 // middle对象构造Move Constructor is called! src: 14835728 // 临时对象通过移动构造函数由middle对象构造DeConstructor is called! // middle对象析构Move Operator is called! src: 14835728 // 对象b通过移动赋值操作符由临时对象赋值DeConstructor is called! // 临时对象析构DeConstructor is called! this-_data: 14835728 // 对象b析构函数调用 在C中对一个变量可以通过const来修饰而const和引用是对变量约束的两种方式为并行存在相互独立。因此就可以划分为了const左值引用、非const左值引用、const右值引用和非const右值引用四种类型。其中左值引用的绑定规则和C98中是一致的。 非const左值引用只能绑定到非const左值不能绑定到const右值、非const右值和const左值。这一点可以通过const关键字的语义来判断。 const左值引用可以绑定到任何类型包括const左值、非const左值、const右值和非const右值属于万能引用类型。其中绑定const右值的规则比较少见但是语法上是可行的比如const int a 1只是我们一般都会直接使用int a 1了。 非const右值引用不能绑定到任何左值和const右值只能绑定非const右值。 const右值引用类型仅是为了语法的完整性而设计的 比如可以使用const MyString right_ref foo()但是右值引用类型的引入主要是为了移动语义而移动语义需要右值引用是可以被修改的因此const右值引用类型没有实际意义。 我们通过表格的形式对上文中提到的四种引用类型可以绑定的类型进行总结。 引用类型/是否绑定 非const左值 const左值 非const右值 const右值 备注   非const左值引用 | 是 | 否 | 否 | 否 |无 | const左值引用 | 是 | 是 | 是 | 是 | 全能绑定类型绑定到const右值的情况比较少见 | 非const右值引用 | 否 | 否 | 是 | 否 | C11中引入的特性用于移动语义和完美转发 | const值引用 | 是 | 否 | 否 | 否 | 没有实际意义为了语法完整性而存在 | 下面针对上述例子我们看一下foo函数绑定参数的情况。 如果只实现了void foo(MyString str)而没有实现void fun(MyString str)则和之前一样foo函数的实参只能是非const左值。 如果只实现了void foo(const MyString str)而没有实现void fun(MyString str)则和之前一样foo函数的参数即可以是左值又可以是右值因为const左值引用是万能绑定类型。 如果只实现了void foo(MyString str)而没有实现void fun(MyString str)则foo函数的参数只能是非const右值。 强制移动语义std::move() 前文中我们通过右值引用给类增加移动构造函数和移动赋值操作符已经解决了函数返回类对象效率低下的问题。那么还有什么问题没有解决呢 在C98中的swap函数的实现形式如下在该函数中我们可以看到整个函数中的变量a、b、c均为左值无法直接使用前面移动语义。 1234567 template class T void swap ( T a, T b ){ T c(a); ab;bc;} 但是如果该函数中能够使用移动语义是非常合适的仅是为了交换两个变量却要反复申请和释放资源。按照前面的知识变量c不可能为非const右值引用因为变量a为非const左值非const右值引用不能绑定到任何左值。 在C11的标准库中引入了std::move()函数来解决该问题该函数的作用为将其参数转换为右值。在C11中的swap函数就可以更改为了 1234567 template class T void swap (T a, T b){T c(std::move(a)); astd::move(b); bstd::move(c);} 在使用了move语义以后,swap函数的效率会大大提升我们更改main函数后测试如下: 1234567891011121314151617 int main() { // move函数 MyString d(123); MyString e(456);swap(d, e); return 1;}// 输出结果通过输出结果可以看出对象交换是成功的Constructor is called! this-_data: 38469648 // 对象d构造Constructor is called! this-_data: 38469680 // 对象e构造Move Constructor is called! src: 38469648 // swap函数中的对象c通过移动构造函数构造Move Operator is called! src: 38469680 // swap函数中的对象a通过移动赋值操作符赋值Move Operator is called! src: 38469648 // swap函数中的对象b通过移动赋值操作符赋值DeConstructor is called! // swap函数中的对象c析构DeConstructor is called! this-_data: 38469648 // 对象e析构DeConstructor is called! this-_data: 38469680 // 对象d析构 右值引用和右值的关系 这个问题就有点绕了需要开动思考一下右值引用和右值是啥含义了。读者会凭空的认为右值引用肯定是右值其实不然。我们在之前的例子中添加如下代码并将main函数进行修改如下 123456789101112131415161718192021 void test_rvalue_rref(MyString str){ cout tmp object construct start endl;MyString tmp str; cout tmp object construct finish endl;}int main() {test_rvalue_rref(foo()); return 1;}// 输出结果Constructor is called! this-_data: 28913680Move Constructor is called! src: 28913680DeConstructor is called!tmp object construct startCopy Constructor is called! src: 28913680 dst: 28913712 // 可以看到这里调用的是复制构造函数而不是移动构造函数tmp object construct finishDeConstructor is called! this-_data: 28913712DeConstructor is called! this-_data: 28913680 我想程序运行的结果肯定跟大多数人想到的不一样“Are you kidding me?不是应该调用移动构造函数吗为什么调用了复制构造函数”。关于右值引用和左右值之间的规则是 如果右值引用有名字则为左值如果右值引用没有名字则为右值。 通过规则我们可以发现在我们的例子中右值引用str是有名字的因此为左值tmp的构造会调用复制构造函数。之所以会这样是因为如果tmp构造的时候调用了移动构造函数则调用完成后str的申请的内存自己已经不可用了如果在该函数中该语句的后面在调用str变量会出现我们意想不到的问题。鉴于此我们也就能够理解为什么有名字的右值引用是左值了。如果已经确定在tmp构造语句的后面不需要使用str变量了可以使用std::move()函数将str变量从左值转换为右值这样tmp变量的构造就可以使用移动构造函数了。 而如果我们调用的是MyString b foo()语句由于foo()函数返回的是临时对象没有名字属于右值因此b的构造会调用移动构造函数。 该规则非常的重要要想能够正确使用右值引用该规则必须要掌握否则写出来的代码会有一个大坑。 完美转发 前面已经介绍了本文的两大主题之一的移动语义还剩下完美转发机制。完美转发机制通常用于库函数中至少在我的工作中还是很少使用的。如果实在不想理解该问题可以不用向下看了。在泛型编程中经常会遇到的一个问题是怎样将一组参数原封不动的转发给另外一个函数。这里的原封不动是指如果函数是左值那么转发给的那个函数也要接收一个左值如果参数是右值那么转发给的函数也要接收一个右值如果参数是const的转发给的函数也要接收一个const参数如果参数是非const的转发给的函数也要接收一个非const值。 该问题看上去非常简单其实不然。看一个例子 1234567891011121314151617181920212223242526 #include iostreamusing namespace std;void fun(int ) { cout lvalue ref endl; } void fun(int ) { cout rvalue ref endl; } void fun(const int ) { cout const lvalue ref endl; } void fun(const int ) { cout const rvalue ref endl; }templatetypename Tvoid PerfectForward(T t) { fun(t); } int main(){PerfectForward(10); // rvalue ref int a;PerfectForward(a); // lvalue refPerfectForward(std::move(a)); // rvalue ref const int b 8;PerfectForward(b); // const lvalue refPerfectForward(std::move(b)); // const rvalue ref return 0;} 在上述例子中我们想达到的目的是PerfectForward模板函数能够完美转发参数t到fun函数中。上述例子中的PerfectForward函数必然不能够达到此目的因为PerfectForward函数的参数为左值类型调用的fun函数也必然为void fun(int )。且调用PerfectForward之前就产生了一次参数的复制操作因此这样的转发只能称之为正确转发而不是完美转发。要想达到完美转发需要做到像转发函数不存在一样的效率。 因此我们考虑将PerfectForward函数的参数更改为引用类型因为引用类型不会有额外的开销。另外还需要考虑转发函数PerfectForward是否可以接收引用类型。如果转发函数PerfectForward仅能接收左值引用或右值引用的一种那么也无法实现完美转发。 我们考虑使用const T t类型的参数因为我们在前文中提到过const左值引用类型可以绑定到任何类型。但是这样目标函数就不一定能接收const左值引用类型的参数了。const左值引用属于左值非const左值引用和非const右值引用是无法绑定到const左值的。 如果将参数t更改为非const右值引用、const右值也是不可以实现完美转发的。 在C11中为了能够解决完美转发问题引入了更为复杂的规则引用折叠规则和特殊模板参数推导规则。 引用折叠推导规则 为了能够理解清楚引用折叠规则还是通过以下例子来学习。 12345678910 typedef int TR;int main(){ int a 1; int b a; int c a; // 编译器报错不可以对引用再显示添加引用TR d a; // 通过typedef定义的类型隐式添加引用是可以的 return 1;} 在C中不可以在程序中对引用再显示添加引用类型对于int c的声明变量方式编译器会提示错误。但是如果在上下文中包括使用模板实例化、typedef、auto类型推断等出现了对引用类型再添加引用的情况编译器是可以编译通过的。具体的引用折叠规则如下可以看出一旦引用中定义了左值类型折叠规则总是将其折叠为左值引用。这就是引用折叠规则的全部内容了。另外折叠规则跟变量的const特性是没有关系的。 1234 A AA AA AA A 特殊模板参数推导规则 下面我们再来学习特殊模板参数推导规则考虑下面的模板函数模板函数接收一个右值引用作为模板参数。 12 templatetypename Tvoid foo(T); 说白点特殊模板参数推导规则其实就是引用折叠规则在模板参数为右值引用时模板情况下的应用是引用折叠规则的一种情况。我们结合上文中的引用折叠规则 如果foo的实参是上文中的A类型的左值时T的类型就为A。根据引用折叠规则最后foo的参数类型为A。如果foo的实参是上文中的A类型的右值时T的类型就为A。根据引用折叠规则最后foo的参数类型为A。 解决完美转发问题 我们已经学习了模板参数为右值引用时的特殊模板参数推导规则那么我们利用刚学习的知识来解决本文中待解决的完美转发的例子。 123456789101112131415161718192021222324252627282930 #include iostreamusing namespace std;void fun(int ) { cout lvalue ref endl; }void fun(int ) { cout rvalue ref endl; }void fun(const int ) { cout const lvalue ref endl; }void fun(const int ) { cout const rvalue ref endl; }//templatetypename T//void PerfectForward(T t) { fun(t); }// 利用引用折叠规则代替了原有的不完美转发机制templatetypename Tvoid PerfectForward(T t) { fun(static_castT (t)); }int main(){PerfectForward(10); // rvalue ref折叠后t类型仍然为T int a;PerfectForward(a); // lvalue ref折叠后t类型为T PerfectForward(std::move(a)); // rvalue ref折叠后t类型为T const int b 8;PerfectForward(b); // const lvalue ref折叠后t类型为const T PerfectForward(std::move(b)); // const rvalue ref折叠后t类型为const T return 0;} 例子中已经对完美转发的各种情况进行了说明这里需要对PerfectForward模板函数中的static_cast进行说明。static_cast仅是对传递右值时起作用。我们看一下当参数为右值时的情况这里的右值包括了const右值和非const右值。 1234567 // 参数为右值引用折叠规则引用前templateint Tvoid PerfectForward(int t) { fun(static_castint (t)); }// 引用折叠规则应用后templateint Tvoid PerfectForward(int t) { fun(static_castint (t)); } 可能读者仍然没有发现上述例子中的问题“不用static_cast进行强制类型转换不是也可以吗”。别忘记前文中仍然提到一个右值引用和右值之间关系的规则如果右值引用有名字则为左值如果右值引用没有名字则为右值。。这里的变量t虽然为右值引用但是是左值。如果我们想继续向fun函数中传递右值就需要使用static_cast进行强制类型转换了。 其实在C11中已经为我们封装了std::forward函数来替代我们上文中使用的static_cast类型转换该例子中使用std::forward函数的版本变为了 12 templatetypename Tvoid PerfectForward(T t) { fun(std::forwardT(t)); } 对于上文中std::move函数的实现也是使用了引用折叠规则实现方式跟std::forward一致。 引用 《深入理解C11-C11新特性解析与应用》C11 标准新特性: 右值引用与转移语义如何评价 C11 的右值引用Rvalue reference特性C11 完美转发C Rvalue References Explained详解C右值引用 对C Rvalue References Explained的翻译 欢迎订阅我的公众号 # C
http://www.pierceye.com/news/242016/

相关文章:

  • 建立外贸网站多少钱淮北招聘网最新招聘信息
  • 有做浏览单的网站jsp网站开发过程
  • 做网站用小型机或服务器wordpress 喜欢
  • 网站建设与维护采访稿中国建设银行电脑版
  • 企业网站建设变相收取等级保护费手游平台十大排名
  • 影响力网站建设恩施网站开发
  • 美术馆网站建设总体要求承德信息发布微信平台
  • 同城便民网站开发为什么企业需要建设网站
  • 网站制作推荐新鸿儒黄山游玩攻略及费用
  • 二手车网站的建设app与微网站的区别是什么
  • 深圳做棋牌网站建设哪家便宜网站域名更改后怎么做映射
  • 长沙网站seo公司知名网站设计服务商
  • 网站建设会议讲话lol视频网站源码
  • 深圳市哪些公司做网站好wordpress小插件下载地址
  • 佛山优化网站公司网站策划书格式及范文
  • 上海网站建设公司秦皇岛网站seo
  • 外贸网站推广 sit淮安市广德育建设网站
  • 准备建网站该怎么做淘宝店铺
  • 1688外贸网站国外购物网站哪个最好
  • 怎么修改网站关键词网站建设的地方
  • 江苏运营网站建设业务淘宝推广引流方法有哪些
  • 快手评论点赞网站建设专业分站微信小程序开发者中心
  • mvc5网站开发之六 管理员p2网站模板
  • 黄页网站推广公司网站建设公司包括哪些内容
  • 网站平台建设目标修改网站j广州网络公司
  • 网站制作商城正规免费发布信息网站
  • 建设企业网站的人员组成莱芜网站建设费用
  • 长春建站网站西宁做网站君博专注
  • 学校实验室网站建设现状怎么做网站 ppt
  • 网站建设骗子公司新开传奇网站发布网