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

怎么做网站海报苏州市住房和城乡建设部网站

怎么做网站海报,苏州市住房和城乡建设部网站,网站内容建设ppt模板,搜索网站的软件有哪些c11 为了提高效率#xff0c;引入了右值引用及移动语义#xff0c;这个概念不太好理解#xff0c;需要仔细研究一下#xff0c;下文会一并讲讲左值、右值、左值引用、右值引用、const 引用、移动构造、移动赋值运行符 … 这些概念。 左值和右值 左值和右值是表达式的属性。…c11 为了提高效率引入了右值引用及移动语义这个概念不太好理解需要仔细研究一下下文会一并讲讲左值、右值、左值引用、右值引用、const 引用、移动构造、移动赋值运行符 … 这些概念。 左值和右值 左值和右值是表达式的属性。从 c 语言开始就有左值、右值这两个名词当时的用途也很简单就是帮助记忆左值可以位于赋值语句的左侧而右值不能。 c 的表达式也只有左值和右值大体也是相似的意思。简单的理解有地址内存位置的对象就是一个左值比如 int i 3i 是一个左值它是有地址的而 3 是右值它是个字面值没有地址。有时候左值可以作为右值这时候用的是它的值比如这样int i 3; int j i;当用 i 去初始化 j 的时候它是作为右值出现的这时候用的是 i 的值而不是 i 的地址内存位置。 所以可以简单的归纳一下当一个对象被用作右值时用的是对象的值内容当对象被用作左值时用的是对象的地址内存位置。[1] 运算符的运算对象和运算结果 赋值运算符运算对象是左值运算结果也是左值。 取地址符运算对象是左值运算结果是右值。 内置解引用运算符、下标运算符、迭代器解引用运算符、stringvector的下标运算符运算对象是左值运算结果也是左值。 内置类型和迭代器的递增递减运算符运算对象是左值运算结果前置版本是左值后置版本是右值。 解引用运行符就是 * 操作符用于获得指针所指的对象比如: int v 100; int* p v; *p 200;p 是一个指向了对象的指针则 *p 就是获得指针 p 所指的对象比如 *p 100; 递增递减的前置和后置版本的具体区别 前置版本比如 i 返回的是左值过程是直接把 i 加 1然后返回 i。后置版本比如 i 返回的是右值过程是先用一个临时变量保存 i 的值然后把 i 值加1然后返回临时变量。 所以建议是除非必须不要使用递增递减的后置版本它们生成了临时变量是一种浪费。 左值引用 左值引用就是绑定到左值上的引用用 表示。c11 之前引用都是左值引用。左值引用就相当于给一个左值对象取一个别名。 它与指针是有显著区别的指针可以指向 NULL 对象指针可以只声明不初始化但左值引用都不行。左值引用必须引用一个已经存在的对象必须定义时初始化像这样: int i 100; int refi i; // 合法 int refi2; // 不合法另外左值引用不能绑定到临时对象上 int i 100; // 不合法 string s {hello}; // 不合法const 引用 const 引用是一种特殊的左值引用与常规左值引用的区别在于它可以绑定到临时对象 const int i1 100; // 合法相当于int temp 100; const int i1 temp; const string s1 {hello}; // 合法相当于string temp {hello}; const string s1 {temp};c 只会为 const 引用产生临时对象不会对非 const 引用产生临时对象这一特性导致了一些容易让人困惑的现象 void f1(const string s) {cout s endl; }void f2(string s) {cout s endl; }f1(hello); // 正常hello 转换成 string 类型的临时对象临时对象可以被 const 引用 引用 f2(hello); // 编译报错hello 转换成 string 类型的临时对象临时对象不可以被 左值引用 引用// 会报类似这样的编译错误no known conversion from const char[6] to string string s hello; f1(s); // 正常一个左值可以被 左值引用 所引用 f2(s); // 正常一个左值可以被 const引用 所引用右值引用 右值引用是 c11 引入的新概念就是绑定到右值上的引用用 表示按流行的说法右值引用是绑定到“一些即将销毁的对象上”。 右值引用只能绑定到右值上不能绑定到左值上举些例子 int r 100; int r1 r; // 不合法r 是一个左值 int r2 100; // 合法100 是一个右值 string s1 {hello}; // 合法hello 是一个右值 string s2 {s1}; // 不合法s1 是一个左值string 右值引用 只是它的类型它本质是上一个左值它是有地址内存位置的这点很容易犯错int x 100; int x1 x; // 不合法x 返回的是左值 int x2 x; // 合法x 返回的是右值虽然可以但项目中不要这么写容易被别人打上面例子中要特别注意的情况是string s1 {hello};在这里s1 是一个类型为 “string 右值引用” 的左值当我们把 右值引用 当成一种类型之后就比较好理解 s1 是一个左值的事实了它是地址内存位置的变量。再举一个例子void f(int p1);在这个函数声明中p1 是一个类型为 int 右值引用 的左值。可归纳如下 1、变量都是左值。 2、函数的形参都是左值。 3、临时对象都是右值。 下面是最重要的问题为什么会需要右值引用 简单的说右值引用的目的在于提高运行效率把对象复制变成对象移动。 这个过程是怎么发生的呢下文接着讲。 移动语义移动构造函数、移动赋值运算符函数 对象的移动是如何发生的在 c11 中是通过移动构造函数和移动赋值运算符来实现的这两个函数与拷贝构造函数和拷贝赋值运算符是相对的。前者的参数是右值引用而后者的参数是左值引用。 如果没有定义移动函数或者源对象不是右值用一个对象给另一个对象初始化或赋值调用的都是拷贝函数。 如果定义了移动函数并且源对象是右值用一个对象给另一个对象初始化或赋值调用的都是移动函数。 复制对象的基本模式是目标对象往往需要 new 一块内存出来然后从源对象那里复制内存数据。 移动对象的基本模式是直接挪用内存不 new 内存也不拷贝数据直接把源对象的内存数据拿来用其代价往往只是一些指针变量的赋值。 显而易见如果有比较多的内存需要拷贝移动对象的效率是更高很多的。 下面举个例子证明以上的说法 源码可在此找到https://github.com/antsmallant/antsmallant_blog_demo/tree/main/blog_demo/modern-cpp 。 // move_constructor_demo.cpp // 编译执行g -stdc14 move_constructor_demo.cpp ./a.out // 屏蔽rvo的编译执行g -stdc14 -fno-elide-constructors move_constructor_demo.cpp ./a.out#include iostream #include vector using namespace std;class A { private:vectorint* p; public:A() {cout A 构造函数无参数 endl;p new vectorint();}// 构造函数A(int cnt, int val) {cout A 构造函数带参数 endl;p new vectorint(cnt, val);}// 析构函数~A() {if (p ! nullptr) {delete p;p nullptr;cout A 析构函数释放 p endl;} else {cout A 析构函数不需要释放 p endl;}}// 拷贝构造函数A(const A other) {cout A 拷贝构造函数 endl;p new vectorint(other.p-begin(), other.p-end());}// 拷贝赋值运算符A operator (const A other) {cout A 拷贝赋值运算符 endl;if (p ! nullptr) {cout A 拷贝赋值前释放旧内存 endl;delete p;p nullptr;}p new vectorint(other.p-begin(), other.p-end()); return *this;}// 移动构造函数A(A other) noexcept {cout A 移动构造函数 endl;this-p other.p; // 挪用别人的other.p nullptr; // 置空别人的}// 移动赋值运算符A operator (A other) noexcept {cout A 移动赋值运算符 endl;this-p other.p; other.p nullptr; return *this;} };void test_copy_constructor() {A a(10, 100);A b(a); }void test_copy_assign_operator() {A a(10, 100);A b;b a; }A getA(int cnt, int val) {return A(cnt, val); }void test_move_constructor() {A a(getA(10, 200)); }void test_move_assign_operator() {A a;a getA(10, 200); }int main() {cout 测试拷贝构造: endl;test_copy_constructor();cout endl 测试拷贝赋值运算符: endl;test_copy_assign_operator();cout endl 测试移动构造: endl;test_move_constructor();cout endl 测试移动赋值运算符: endl;test_move_assign_operator();return 0; }输出是 测试拷贝构造: A 构造函数带参数 A 拷贝构造函数 A 析构函数释放 p A 析构函数释放 p测试拷贝赋值运算符: A 构造函数带参数 A 构造函数无参数 A 拷贝赋值运算符 A 拷贝赋值前释放旧内存 A 析构函数释放 p A 析构函数释放 p测试移动构造: A 构造函数带参数 A 析构函数释放 p测试移动赋值运算符: A 构造函数无参数 A 构造函数带参数 A 移动赋值运算符 A 析构函数不需要释放 p A 析构函数释放 poops上面的测试可以说有 75% 成功了关于移动构造的测试失败了它压根没调用移动构造函数。怎么回事 这实际上是一种编译器优化叫 RVOReturn Value Optimization返回值优化这个我们下文再具体讲讲为了避免这种优化对于我们测试的影响我们可以给编译器传递一个选项暂时禁用这种优化修改一下编译命令 g -stdc14 -fno-elide-constructors move_constructor_demo.cpp ./a.out重新编译运行移动构造的测试输出变成 测试移动构造: A 构造函数带参数 A 移动构造函数 A 析构函数不需要释放 p A 移动构造函数 A 析构函数不需要释放 p A 析构函数释放 p虽然如愿输出了 “A 移动构造函数”但输出有点多。把代码列出来简单分析一下 A getA(int cnt, int val) {// 1、用带参数的构造函数 A(10, 200) 生成一个局部对象 x// 2、return 的时候用移动构造函数 A(x) 生成一个临时对象 treturn A(cnt, val); }void test_move_constructor() {// 3、用移动构造函数 A(t) 生成局部对象 aA a(getA(10, 200)); }最精准的分析需要看汇编代码但汇编有点复杂这里先不看等下篇文章讲 RVO 的时候再一并用汇编分析。 编译器默认生成的移动构造/赋值运算符函数 如果我们没有自己写拷贝构造函数或拷贝赋值运算符那么编译器会帮我们生成默认的。 编译器在特定条件下也会帮我们生成默认的移动函数[2] 一个类没有定义任何版本的拷贝构造函数、拷贝赋值运算符、析构函数类的每个非静态成员都可以移动 内置类型如整型、浮点型定义了移动操作的类类型 第1点应该是确保系统可以生成符合程序员需要的移动函数如果代码中定义了那三种函数说明程序员有自己控制复制或释放的倾向这时候编译器就不默认生成了。 第2点只有确保成员都可移动才能生成正确的移动函数。 std::move 上面讲移动构造和移动赋值运算符的时候发现由于编译器的优化RVO导致即使我们构造了合适的场景也没能验证移动构造的使用。 接下来介绍的 std::move即使不屏蔽 RVO也可以验证移动构造的使用只需要这样修改 // g -stdc14 move_constructor_demo.cpp ./a.outvoid test_move_constructor_use_stdmove() {A x(10, 200);A a(std::move(x)); }int main() {cout endl 使用 std::move 测试移动构造 endl;test_move_constructor_use_stdmove(); }输出: A 构造函数带参数 A 移动构造函数 A 析构函数释放 p A 析构函数不需要释放 pstd::move 把 x 转换成了一个右值类型的变量所以编译器使用移动构造函数来生成变量 a。 特别注意std::move 并不完成对象的移动它的作用只是把传递进去的实参转换成一个右值可以理解它是某种 cast 封装一种可能的实现如下[3] templatetypename T typename remove_referenceT::type move(T param) {using ReturnType typename remove_referenceT::type;return static_castReturnType(param); }实参可以是左值也可以是右值: int a 100; int r1 std::move(a); // 合法 int r2 std::move(200); // 合法std::move 只是完成类型转换真正起作用的是移动构造函数或移动赋值运算符函数在这两个函数中写移动逻辑。 综上std::move 是一种危险的操作调用时必须确认源对象没有其他用户了否则容易发生一些意外的难以理解的状况。 参考 [1] [美] Stanley B. Lippman, Josée Lajoie, Barbara E. Moo. C Primer 中文版第 5 版. 王刚, 杨巨峰. 北京: 电子工业出版社, 2013-9: 120, 154, 182. [2] 王健伟. C新经典. 北京: 清华大学出版社, 2020-08-01. [3] [美]Scott Meyers. Effective Modern C(中文版). 高博. 北京: 中国电力出版社, 2018-4: 149, 151.
http://www.dnsts.com.cn/news/235552.html

相关文章:

  • 怎样设计个人网站太原网站推广优化
  • 上海做高端网站制作爱站网站长工具
  • 想学Wordpress建站即墨做砍价小程序最好的网站
  • 现在企业做网站一般用什么框架wordpress 正文宽度
  • wordpress编辑器无法实现随意排版seo怎么做关键词排名
  • 做网站的人属于什么行业网站编辑怎么做内容分类
  • 做网站例子宁波关键词优化品牌
  • 公司微网站怎么做的好网站如何做美工
  • 网站建设的种类网站开发产品描述
  • 承德建设局网站可作外链的网站
  • 德州市住房和城乡建设局网站浙江网站建设实验心得
  • 怎么用自己的电脑做网站网站建设交接函
  • ps网站首页效果图实例网站风格定位有哪些
  • 微信网站制作系统网站的用户登录一般怎么做的
  • 电子商务网站建设期末考试营销型网站单页
  • 网站 备案 中国 名字ps网页设计效果图
  • 网站宣传与推广的方法ps网站建设要知道的知识
  • 做网站公司(信科网络)谷歌网站怎么做排名
  • 有哪些网站是做分期付款的湖南建设长沙网站建设价格
  • 网站建设合同要缴纳印花税吗凌云县城乡建设局网站
  • 短视频网站开发教程寻找做网站的公司
  • 国际大型门户网站做网上招聘哪个网站好
  • 网站主关键词如何优化前端做网站需要学什么
  • 小说网站开发技术实现html个人网页完整代码展示
  • 做网站找不到客户为什么亿唐网不做网站做品牌
  • 怎么仿别人的网站网站建设推进会讲话稿
  • 网站点击率怎么建网店运营推广职业技能等级证书
  • 洮南网站建设企业年金如何提取
  • 大气物流网站模块建筑网页
  • 招聘网站分析如何做百度关键字排名软件