C++移动语义介绍与使用讲解

目录
  • 引入移动语义
  • std::move

引入移动语义

为了能够理解移动语义的目的,我们先从整成的一个类进行示范,示例如下:

class TestClass
{
public:
    TestClass(int s) :m_number(s) {
        cout << "constructor!\n";
    }
    ~TestClass() {
        cout << "destructor!\n";
    }
    // 拷贝构造
    TestClass(const TestClass& that) :m_number(that.m_number) {
        cout << "copy constructor!\n";
    }
    // 赋值操作符
    TestClass& operator=(const TestClass& tc) {
        cout << "operator= is called\n";
        if (this == &tc)
            return *this;
        m_number = 0;
        m_number = tc.m_number;
        return *this;
    }
    int m_number;
};
TestClass tcFactory()
{
    TestClass tc(10);
    return tc;
}
int main()
{
    {
        TestClass tc = tcFactory();
    }
    return 0;
}

上面代码的输出结果如下:

constructor!
copy constructor!
destructor!
destructor!

可以看到进行了一次构造和一次拷贝构造,拷贝构造就发生在tc 接收tcFactory()返回值时。这几产生了不必要的资源消耗,如果这里可以重用或者转移return产生的临时值(右值)是不是可以减少资源的消耗呢?C++正是使用转移的方式来处理的,这个转移就是移动构造函数,也可以说是移动语义,示例如下:

// 其他代码不变,只增加移动构造与移动赋值处理函数
// 移动构造
TestClass(TestClass&& rr):m_number(rr.m_number) {
    // 如果这里是指针的变量的话则可以避免指针重复释放的问题
    rr.m_number = 0;
    cout << "move constructor!\n";
}
// 移动赋值
TestClass& operator=(TestClass&& rr) {
    cout << "move operator= is called\n";
    if (this == &rr)
        return *this;
    // 此步骤相当于对源指针的释放
    m_number = 0;
    m_number = rr.m_number;
    return *this;
}

添加上述代码后,输出结果如下:

constructor!
move constructor!
destructor!
destructor!

此时第二次调用的就是移动构造,这样可以直接使用右值,避免重新申请空间,调用两次析构是因为,临时对象是被延长了声明周期,但最终也是要释放的。

std::move

前面看到移动构造接收的是右值引用,那么在需要对左值进行移动语义的时候(进行移动语义后,此左值以后将失效),那么就必须将左值转换为右值。此时td::move就很好的完成了这件事情,示例如下:

int main()
{
    vector<int> v{ 1,2,3,4 };
    // 拷贝构造
    vector<int> v1 = v;
    // 移动构造
    vector<int> v2 = std::move(v);
    cout << "v size():" << v.size() << "\n";
    cout << "v2 size():" << v2.size() << "\n";
    return 0;
}

上面输出代码为:

v size():0
v2 size():4

关于std::move注意的几点:

std::move本质上只是将传入的参数转换为一个右值,使用static_cast进行转换

std::move在进行类型推导时会保留形参的const属性,此时会造成一种使用失效的场景如下:

class TestClass
{
public:
	// 这么写在 VS中也会提示 	C26478 不要对常量变量使用 std::move
    TestClass(const string& str) :m_str(std::move(str)) {
    }
    string m_str;
};
int main()
{
    string str = "sss";
    TestClass tc(str);
    cout << tc.m_str << "\n";
    // 此处应该输出空,但实际并非如此 , 两个输出都是 sss
    cout << str << "\n";
    return 0;
}

到此这篇关于C++移动语义介绍与使用讲解的文章就介绍到这了,更多相关C++移动语义内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 详解C++11中的右值引用与移动语义

    C++11的一个最主要的特性就是可以移动而非拷贝对象的能力.很多情况都会发生对象的拷贝,有时对象拷贝后就立即销毁,在这些情况下,移动而非拷贝对象会大幅度提升性能. 右值与右值引用 为了支持移动操作,新标准引入了一种新的引用类型--右值引用,就是必须绑定到右值的引用.我们通过&&而不是&来获得右值引用.右值引用一个重要的特性就是只能绑定到将要销毁的对象. 左值和右值是表达式的属性,一些表达式生成或要求左值,而另一些则生成或要求右值.一般而言,一个左值表达式表示的是一个对象的身份,而右

  • C++11中value category(值类别)及move semantics(移动语义)的介绍

    前言 C++11之前value categories只有两类,lvalue和rvalue,在C++11之后出现了新的value categories,即prvalue, glvalue, xvalue.不理解value categories可能会让我们遇到一些坑时不知怎么去修改,所以理解value categories对于写C++的人来说是比较重要的.而理解value categories离不开一个概念--move semantics.了解C++11的人我相信都了解了std::move,右值引用

  • 深入了解c++11 移动语义与右值引用

    1.移动语义 C++11新标准中一个最主要的特性就是提供了移动而非拷贝对象的能力.如此做的好处就是,在某些情况下,对象拷贝后就立即被销毁了,此时如果移动而非拷贝对象会大幅提升性能.参考如下程序: //moveobj.cpp #include <iostream> #include <vector> using namespace std; class Obj { public: Obj(){cout <<"create obj" << e

  • C++ move semantic移动语义介绍

    目录 前言 移动构造 为什么我们需要move semantic 前言 在说移动语义之前 本文作者假设你已经具备了深拷贝浅拷贝左值右值等基本概念 本文不会再过多叙述 那么接下来 让我们开始吧 Tips:(警告 警告 警告 警告)在阅读本文章之前 作者首先提醒 线代编译器有RVO和NRVO等一系列优化策略 除非你明确知道你要使用std::move 不然我并不是很推荐你使用移动语义 他很有可能是无意义的 移动构造 在说移动语义之前 让我们先来说说移动构造这玩意 我们都知道 深拷贝是会把在堆区的内存一起

  • C++学习之移动语义与智能指针详解

    移动语义 1.几个基本概念的理解 (1)可以取地址的是左值,不能取地址的就是右值,右值可能存在寄存器,也可能存在于栈上(短暂存在栈)上 (2)右值包括:临时对象.匿名对象.字面值常量 (3)const 左值引用可以绑定到左值与右值上面,称为万能引用.正因如此,也就无法区分传进来的参数是左值还是右值. const int &ref = a;//const左值引用可以绑定到左值 const int &ref1 = 10;//const左值引用可以绑定到右值 (4)右值引用:只能绑定到右值不能绑

  • C++左值与右值,右值引用,移动语义与完美转发详解

    目录 C++——左值与右值.右值引用.移动语义与完美转发 一.左值和右值的定义 二.如何判断一个表达式是左值还是右值(大多数场景) 三.C++右值引用 四.std::move()与移动语义 五. 完美转发 总结 C++——左值与右值.右值引用.移动语义与完美转发 在C++或者C语言中,一个表达式(可以是字面量.变量.对象.函数的返回值等)根据其使用场景不同,分为左值表达式和右值表达式. 一.左值和右值的定义 1.左值的英文为locator value,简写为lvalue,可意为存储在内存中.有明

  • C++模板编程特性之移动语义

    目录 C++的值类型 右值引用与移动构造和移动赋值 C++的值类型 我们知道,每个变量都有类型,或整形或字符型等来进行了分类,不仅如此,C++表达式(带有操作数的操作符.字面量.变量名等)在类型的属性上,还有一种属性,即值类别(value category).且每个表达式只属于三种基本值尖别中的一种:左值(lvalue),右值(rvalue),将亡值(xvalue),每个值类别都与某种引用类型对应. 其中,左值和将亡值成为泛左值(generalized value,gvalue),纯右值和将亡值

  • C++移动语义介绍与使用讲解

    目录 引入移动语义 std::move 引入移动语义 为了能够理解移动语义的目的,我们先从整成的一个类进行示范,示例如下: class TestClass { public: TestClass(int s) :m_number(s) { cout << "constructor!\n"; } ~TestClass() { cout << "destructor!\n"; } // 拷贝构造 TestClass(const TestClass

  • java RMI详细介绍及实例讲解

    java本身提供了一种RPC框架--RMI(即RemoteMethodInvoke远程方法调用),在编写一个接口需要作为远程调用时,都需要继承了Remote,Remote接口用于标识其方法可以从非本地虚拟机上调用的接口,只有在"远程接口"(扩展java.rmi.Remote的接口)中指定的这些方法才可远程使用,下面通过一个简单的示例,来讲解RMI原理以及开发流程: 为了真正实现远程调用,首先创建服务端工程rmi-server,结构如下: 代码说明: 1.User.java:用于远程调用

  • C++标准模板库string类的介绍与使用讲解

    介绍 c++中字符串string对象属于一个类,内置了很多实用的成员函数,操作简单,方便更直观. 命名空间为std,所属头文件<string> 注意:不是<string.h>. 跟进代码会发现string其实只是basic_string模板类的一个typedef. 赋值 //方法1 string str1 = "woniu201"; //方法2 char* p = "woniu201"; string str2 = p; 遍历 //方法1 使

  • 实战讲解Maven安装及基本使用详解

    前言 Apache Maven,是一个跨平台的软件项目管理及自动构建工具,由Apache软件基金会所提供.Maven主要服务于基于Java平台的项目构建.依赖管理和项目信息管理.Maven的目标是能够让开发者在最短的时间内了解项目开发工作的完整状态,主要包括以下几个方面: •简化项目构建过程 •提供一个统一的项目构建系统 •通过POM(project object model)提供一个项目的重要信息 •为最佳项目开发实践提供一个指导方针 接下来本文就通过Maven的安装.基本的指令使用和一个实际

  • java中使用map排序的实例讲解

    对列表进行排序也是我们经常遇到的问题,这里缩小一下范围,使用map来对列表排序.相信大家都有过TreeMap排序的经历,不过Map.Entry能按值进行排序,在用法上略胜一筹.下面我们会对这两种map排序的方法分别进行介绍,着重讲解Map.Entry排序的方法. 1.Map.Entry方法 把Map.Entry放进list,再用Comparator对list进行排序 List list = new ArrayList(map.entrySet()); Collections.sort(list,

  • Java内存模型final的内存语义

    目录 1.final域的重排序规则final 2.写final域的重排序规则 3.读final与的重排序规则 4.final域为引用类型 5.为什么final引用不能从构造函数内"逸出" 6.final语义在处理器中的实现 7.JSR-133为什么要增强final的语义 上篇并发编程之Java内存模型volatile的内存语义介绍了volatile的内存语义,本文讲述的是final的内存语义,相比之下,final域的读和写更像是普通变量的访问. 1.final域的重排序规则final

  • node.js chat程序如何实现Ajax long-polling长链接刷新模式

    废话不多说,开始今天的主题.纵观这个程序,感觉它的最可贵之处,在于展示了,如何用nodejs实现长链接模式的刷新技术. (这个程序不详细介绍,重点讲解这个功能) Client.js 首先看一段核心代码: 复制代码 代码如下: function longPoll (data) { //....此处省略**行 $.ajax({ cache: false , type: "GET" , url: "/recv" , dataType: "json" ,

  • 一篇文章带你从入门到精通:RabbitMQ

    目录 1. 浅浅道来 1.1 什么是中间件? 1.1.1 分布式的概念(补充) 1.2 什么是消息中间件/消息队列(MQ) 1.2.1 消息队列应用场景 1.3 什么是 RabbitMQ 2. 下载与安装 2.1 手动安装 2.1.1 下载安装过程 2.1.2 配置 Web 界面管理 2.1.3 简单介绍 Web 界面管理 2.2 Docker 安装 2.2.1 配置 yum 2.2.2 安装 docker 2.2.3 安装 RabbitMQ (任选其一) 3. RabbitMQ 协议和模型 3

  • java如何对map进行排序详解(map集合的使用)

    今天做统计时需要对X轴的地区按照地区代码(areaCode)进行排序,由于在构建XMLData使用的map来进行数据统计的,所以在统计过程中就需要对map进行排序. 一.简单介绍Map 在讲解Map排序之前,我们先来稍微了解下map.map是键值对的集合接口,它的实现类主要包括:HashMap,TreeMap,Hashtable以及LinkedHashMap等.其中这四者的区别如下(简单介绍): HashMap:我们最常用的Map,它根据key的HashCode 值来存储数据,根据key可以直接

随机推荐