首页 > 编程语言 >现代C++(Modern C++)基本用法实践:三、移动语义

现代C++(Modern C++)基本用法实践:三、移动语义

时间:2023-07-13 13:01:55浏览次数:42  
标签:std Str Modern 语义 C++ str 移动 LOG

概述

移动

移动(move)语义C++引入了一种新的内存优化,以避免不必要的拷贝。在构造或者赋值的时候,如果实参是右值(或者左值由std::move转换成右值),便会匹配移动语义的函数调用如下述举例的Str(Str&& obj)

移动语义的本质是将资源(内存/句柄)转移给另一个对象,被转移资源的对象不应再被使用。(这个概念有点像仙侠小说中的夺舍,夺舍成功的人获取被夺舍的人的身体(资源)),如下面伪代码:

class Obj
{
	data
	Obj(){
		data = malloc(100)
	}
	// 移动 (夺舍)
	Obj(Obj&& other){
		data = other.data
		other.data = nullptr
	}
}

右值

右值直观理解是等号右边的值(大概如此,并不准确),右值的概念指代的东西比较多,大概是指不可寻址的值(也有例外)。我觉得这个不必太过纠结,记住几个常见的即可:

  • 临时对象:如函数返回的临时对象(下面有举例)
  • 字面量
  • 显式std::move()转换的值
  • 没有捕获参数的lambda

C++ 值类别表

在 C++11之后,C++根据

  • 被标识:可通过不同标识符指代同一实体。(对象/内存)
  • 可移动:可作为移动语义函数的参数,例如移动构造,移动赋值。

将值分为以下类别:

  • 泛左值:被标识
    • 左值:被标识且不可移动
    • 将亡值:被标识可移动
  • 右值:可移动
    • 将亡值:被标识可移动
    • 纯右值:不被标识且可移动

用法举例

参考测试项目代码ModernCppTest/modrenc_rvalueref_stdmove.cpp
主要内容:

  • 移动语义下的构造和赋值
  • 移动还是拷贝的重载匹配
  • C++ 优化临时对象(连加产生的中间临时对象)尝试调用移动语义
#include "ModernCppTestHeader.h"
#include <string>
using std::string;

namespace n_rvalueref {
	class Str {
	public:
		Str() {
			LOG("无参构造");
			this->str = new string();
		}

		Str(const string& str) {
			LOG("有参构造 str = " << str);
			this->str = new string(str);
		}

		Str(const Str& obj) {
			LOG("拷贝构造 obj.str = " << *obj.str);
			this->str = new string(*obj.str);
		}

		Str(Str&& obj) noexcept {
			LOG("移动构造 obj.str = " << *obj.str);
			this->str = std::move(obj.str);
			// 被移动的对象不应该再被使用了
			obj.str = nullptr;
		}

		Str& operator=(Str&& v) noexcept {
			LOG("移动语义 operator = ");

			if (this != &v) {
				this->str = std::move(v.str);
			}

			return *this;
		}

		Str operator+(const Str& v)
		{
			string s = *this->str + *v.str;
			return Str(s);
		}

		void Log()
		{
			LOG(str);
		}

		string* str;
	};
}

using n_rvalueref::Str;

// 右值引用&移动语义
void rvalueref_stdmove_test()
{
	LOG_FUNC();

	LOG_TAG("拷贝构造");
	{
		Str t1("A");
		Str t2 = t1;
		LOG_VAR(*t2.str);
	}


	LOG_TAG("移动构造, 注意被移动的对象t1不应再被使用");
	{
		// t1是左值,使用std::move强制转换成右值
		Str t1("A");
		Str t2 = std::move(t1);
		LOG_VAR(*t2.str);
	}


	LOG_TAG("移动语义的运算符重载,注意运算符重载发生赋值运算(这个例子),而不是构造运算(上个例子)");
	{
		Str t1("A");
		Str t2;
		t2 = std::move(t1);
	}


	LOG_TAG("除了上述显示使用std::move转换,常见的容易忽视的发生移动构造场合列举");
	{
		LOG("---1 连续加法产生的临时对象,c++会尝试使用移动语义进行优化");
		Str t1("A");
		Str t2("B");
		Str t3("C");
		Str t4;
		t4 = t1 + t2 + t3;

		LOG("---2 函数返回的临时对象,c++会尝试使用移动语义进行优化");
		auto f = []() {
			auto s = Str("Hi"); 
			return s; 
		};
		Str t5 = f();

		/*
		- 在容器中插入或删除元素:比如 std::vector::push_back,如果传递给它的是右值,它就会使用移动语义。
		- 在标准库算法中:许多标准库算法,比如 std::sort,std::partition 等,在进行元素交换时会使用移动语义。
		- 在 std::swap 中:std::swap 会使用移动语义来交换两个对象。
		*/
	}
}

标签:std,Str,Modern,语义,C++,str,移动,LOG
From: https://www.cnblogs.com/hggzhang/p/17523881.html

相关文章

  • 现代C++(Modern C++)基本用法实践:二、Lambda表达式
    概述lambda表达式,有时也被称为匿名函数。他提供了简短的,内联的函数对象。用法形式如:[capture](parameters)->return_type{body}具体用法如下文举例它的实现是由编译器决定的,在我的编译器上他是通过创建一个匿名类,通过重载()运算符,成为一个可调用对象,从而实现调用,类似://......
  • 现代C++(Modern C++)基本用法实践:七、范围遍历
    概述c++的for循环在语法上有些刻板,近几个版本对此进行了优化,支持了基于范围的for循环用法举例参考测试项目代码ModernCppTest/modrenc_range_for.cpp主要内容:数组遍历vector遍历字符串遍历map遍历#include"ModernCppTestHeader.h"#include<vector>#include<map>......
  • 现代C++(Modern C++)基本用法实践:六、constexpr编译时计算
    概述constexpr修饰的变量、函数、对象构造函数表示在编译时就可以确定。它经常用来计算一些编译期可以确定常数,和常数组成的表。比如编译时确定10000以内所有的素数,运行时用的时候直接查表。用法举例参考测试项目代码ModernCppTest/modrenc_constexpr.cpp主要内容:constexpr......
  • 现代C++(Modern C++)基本用法实践:零、概述&测试项目
    序言习惯上,我们把C++11之前的C++语法特性称之为“传统C++”,而把c++11之后的语法特性称之为现代C++。有一种说法称C++为中级语言,因为它的特性介于低级语言(如各类汇编语言)和高级语言(Python、C#)之间--一般来说,它在运行效率上比高级语言要高,而在开发效率上又比高级语言低一些。随着C......
  • 现代C++(Modern C++)基本用法实践:N、其他零散的常用特性
    概述这一篇简单介绍一些其他的比较实用的特性,如果读者想了解现代C++的全部特性,参考:cppreference其他特性预置和弃置函数default&delete在C++11中引入了default和delete关键字,允许程序员更加明确地控制类的默认操作(如默认构造函数,拷贝构造函数,拷贝赋值运算符,析构函数等)......
  • 现代C++(Modern C++)基本用法实践:八、线程支持
    概述在c++11之前,c++并未对线程编程提供直接的支持。在c++11之后,支持了线程管理、同步、条件变量等支持。在其他的c++库中(例如UE的线程库)还增加了多任务模型的抽象。用法举例参考测试项目的modrenc_auto_decltype.cpp文件主要内容:线程的创建使用future&async进行异步操作......
  • PAT-甲级-1007 Maximum Subsequence Sum C++
    Givenasequenceof K integers{ N1​, N2​,..., N​K }.Acontinuoussubsequenceisdefinedtobe{ Ni​, Ni+1​,..., Nj​ }where 1≤i≤j≤K.TheMaximumSubsequenceisthecontinuoussubsequencewhichhasthelargestsumofitselements.Fore......
  • c++ day 8
    今天终于来学习时间复杂度了当分析算法的时间复杂度时,我们通常关注以下几个方面来确定算法的执行时间:循环次数:循环是算法中常见的结构,它会重复执行一段代码。时间复杂度取决于循环的次数。例如,一个循环从1到n的遍历,时间复杂度就是O(n)。嵌套循环:如果算法中存在多个嵌套循环......
  • 【C++开源库】Windows 下编译 libcurl 库
    Whatislibcurl?libcurl是一个跨平台的网络协议库,支持http,https,ftp,gopher,telnet,dict,file,和ldap协议。libcurl同样支持HTTPS证书授权,HTTPPOST,HTTPPUT,FTP上传,HTTP基本表单上传,代理,cookies和用户认证。想要知道更多关于libcurl的介绍,可以到官网......
  • C++ 数据封装
     所有的C++程序都有以下两个基本要素:程序语句(代码):这是程序中执行动作的部分,它们被称为函数。程序数据:数据是程序的信息,会受到程序函数的影响。封装是面向对象编程中的把数据和操作数据的函数绑定在一起的一个概念,这样能避免受到外界的干扰和误用,从而确保了安全。数据封装......