C++基本用法实践之移动语义详解

 更新时间:2023年07月17日 11:41:52   作者:寡人正在Coding  
移动(move)语义是C++引入了一种新的内存优化,以避免不必要的拷贝,下面小编就来和大家简单聊聊C++中移动语义的相关使用吧,希望对大家有所帮助

概述

移动

移动(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 会使用移动语义来交换两个对象。
		*/
	}
}

到此这篇关于C++基本用法实践之移动语义详解的文章就介绍到这了,更多相关C++移动语义内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • C++中map和set的使用示例详解

    C++中map和set的使用示例详解

    set的底层是用红黑树实现的,增删查的效率是O(logn),迭代器遍历走的是搜索树的中序遍历,所以遍历后的元素是有序的,本文给大家介绍了C++中map和set的使用示例,感兴趣的朋友一起看看吧
    2025-05-05
  • C语言 模拟实现memcpy与memmove函数详解

    C语言 模拟实现memcpy与memmove函数详解

    这篇文章主要介绍了C语言详解如何模拟内存函数,用到了mencpy与memmove两个函数,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步
    2022-04-04
  • C++实现LeetCode(144.二叉树的先序遍历)

    C++实现LeetCode(144.二叉树的先序遍历)

    这篇文章主要介绍了C++实现LeetCode(144.二叉树的先序遍历),本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-07-07
  • C++标准库(std)用法解读

    C++标准库(std)用法解读

    本文介绍了C++标准库的主要组成部分及其使用方法,涵盖命名空间、输入输出流、字符串处理、STL容器、算法、数值处理、函数对象与可调用对象、异常处理、时间日期、文件系统、线程支持和正则表达式等,示例代码展示了如何使用这些功能
    2026-04-04
  • c++中用TINYXML解析XML文件

    c++中用TINYXML解析XML文件

    这篇文章主要介绍了c++中如何用TINYXML解析XML文件,文中案例非常详细,帮助大家更好的了解和学习,感兴趣的朋友可以了解下
    2020-06-06
  • C/C++ - 从代码到可执行程序的过程详解

    C/C++ - 从代码到可执行程序的过程详解

    这篇文章主要介绍了C/C++ - 从代码到可执行程序的过程,主要有预编译和编译,汇编链接,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-01-01
  • c语言中使用BF-KMP算法实例

    c语言中使用BF-KMP算法实例

    这篇文章主要介绍了c语言中使用BF-KMP算法,大家参考使用
    2013-11-11
  • c++的glog与spdlog的性能对比测试分析

    c++的glog与spdlog的性能对比测试分析

    这篇文章主要为大家介绍了c++的glog与spdlog的性能对比测试分析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-05-05
  • 解析C++中虚析构函数的作用

    解析C++中虚析构函数的作用

    本篇文章是对C++中虚析构函数的作用进行了详细的分析介绍,需要的朋友参考下
    2013-05-05
  • C++中String类的常用接口函数总结

    C++中String类的常用接口函数总结

    这篇文章主要介绍了C++中Stirng类的常用接口函数,文中有详细的代码示例供大家参考,对我们学习C++有一定的帮助,感兴趣的同学可以跟着小编一起来学习
    2023-06-06

最新评论