移动构造和移动赋值与std::move

---------------------------------------移动构造--------------------------------------------

传统的深拷贝深赋值

  对于类中,含有指针的情况,要自实现其拷贝构造和拷贝赋值。也就是所谓的深拷贝和深赋值。我想这己经成为一种共识了。

比如如下类:

#include <iostream>
using namespace std;
class HasPtrMem
{
public:
  HasPtrMem():_d(new int(0)){
    cout<<"HasPtrMem()"<<this<<endl;
  } 

HasPtrMem(const HasPtrMem& another)
:_d(new int(*another._d)){
  cout<<"HasPtrMem(const HasPtrMem&
  another)"<<this<<"->"<<&another<<endl;
}

~HasPtrMem(){
  delete _d;
  cout<<"~HasPtrMem()"<<this<<endl;
  } 

  int * _d;
};

HasPtrMem getTemp()
{
  return HasPtrMem();
}

int main(int argc, char *argv[])
{ 

// HasPtrMem a;
// HasPtrMem b(a);
// cout<<*a._d<<endl;
// cout<<*b._d<<endl;
  HasPtrMem&& ret = getTemp();
  return 0;
}

  上面的过程,我们己经知晓,ret 作为右值引用,引用了临时对象,由于临时对象是待返回对象的复本,所以表面上看起来是,待返回对象的作用域扩展了,生命周期也延长了。

从右值引到移动构造

  前面我们建立起来了一个概念,就是右值引用。用右值引用的思想,再来实现一下拷贝。这样,顺便把临时对象的问题也解决了。

#include <iostream>
using namespace std;

class HasPtrMem
{
public:
    HasPtrMem():_d(new int(0)){
    cout<<"HasPtrMem()"<<this<<endl;
} 

HasPtrMem(const HasPtrMem& another)
:_d(new int(*another._d)){
    cout<<"HasPtrMem(const HasPtrMem& another)" <<this<<"->"<<   &another<<endl;
}

HasPtrMem(HasPtrMem &&another)
{
    cout<<this<<" Move resourse from "<<&another<<"->"<< another._d <<endl;
     _d = another._d;
    another._d = nullptr;
} 

~HasPtrMem(){
    delete _d;
    cout<<"~HasPtrMem()"<<this<<endl;
    }
    int * _d;
};

HasPtrMem getTemp()
{
    return HasPtrMem();
} 

int main(int argc, char *argv[])
{
    HasPtrMem a = getTemp();
    return 0;
}    

移动构造

  如下是,移动构造函数。我们借用临时变量,将待返回对象的内容“偷”了过来。

  移动构造充分体现了右值引用的设计思想,通过移动构造我们也在对象层面看清了右值引用的本质。从而对于普通类型右值引用内部是怎样操作的的也就不难理解了。

//移动构造HasPtrMem(HasPtrMem &&another)
{
    cout<<this<<" Move resourse from "<<&another<<"->"<< another._d<<endl;
    _d = another._d;
    another._d = nullptr;
}

  再来看一下拷贝构造函数,我们对比一下区别:

HasPtrMem(const HasPtrMem& another)
:_d(new int(*another._d)){
    cout<<"HasPtrMem(const HasPtrMem& another)" <<this<<"->"<<   &another<<endl;
} 

  移动构造相比于拷贝构造的区别,移动构造通过指针的赋值,在临时对象析构之前,及时的接管了临时对象在堆上的空间地址。

关于默认的移动构造函数

  对于不含有资源的对象来说,自实现拷贝与移动语义并没有意义,对于这样的类型 而言移动就是拷贝,拷贝就是移动。 

  拷贝构造/赋值和移动构造/赋值,必须同时提供或是同时不提供。才能保证同时俱有拷贝和移动语义。只声明一种的话,类只能实现一种语义。

  只有拷贝语义的类,也就是 C++98 中的类。而只有移动语义的类,表明该类的变量所拥有的资源只能被移动,而不能被拷贝。那么这样的资源必须是唯一的。只有移动语义构造的类型往往是“资源型”的类型。比如智能指针,文件流等。

 效率问题

#include <iostream>

using namesapce std;

class Copyable
{
public:
    Copyable(int i)
        :_i(new int(i))
    {
        cout<<"Copyable(int i):"<<this<<endl;
    }

    Copyable(const Copyable & another)
        :_i(new int(*another._i))
    {
        cout<<"Copyable(const Copyable & another):"<<this<<endl;
    }

    Copyable(Copyable && another)
    {
        cout<<"Copyable(Copyable && another):"<<this<<endl;
        _i = another._i;
    }
    Copyable & operator=(const Copyable &another)
    {
        cout<<"Copyable & operator=(const Copyable &another):"<<this<<endl;
        if(this == & another)
            return *this;
        *_i=*another._i;
        return *this;
    }

    Copyable & operator=(Copyable && another)
    {
        cout<<"Moveable & operator=(Moveable && another):"<<this<<endl;
        if(this != &another)
        {
            *_i = *another._i;
            another._i = NULL;
        }
        return * this;
    }

    ~Copyable()
    {
        cout<<"~Copyable():"<<this<<endl;
        if(_i)
            delete _i;
    }

    void dis()
    {
        cout<<"class Copyable is called"<<endl;
    }
    void dis() const
    {
        cout<<"const class Copyable is called"<<endl;
    }

private:
    int * _i;
};

void putRRValue(Copyable && a)
{
    cout<<"putRRValue(Copyable && a)"<<endl;
    a.dis();
}

void putCLValue(const Copyable & a)
{
    cout<<"putCRValue(Copyable & a)"<<endl;
    a.dis();//error!
}

//const T&和T&&重载同时存在先调用谁?
void whichCall(const Copyable & a)
{
    a.dis();
}

void whichCall(Copyable && a)
{
    a.dis();
}

int main(int argc, char *argv[])
{
//    Copyable rrc = getCopyable();

    cout<<"调用移动构造"<<endl;
    Copyable a =Copyable(2);//匿名对象/临时对象优先调用右值引用 构造-右值构造

    cout<<"调拷贝构造"<<endl;
    Copyable ca(a);

    cout<<"直接构造右值"<<endl;
    Copyable && rra =Copyable(2);

    cout<<"=================================="<<endl;

    //右值引用与const引用。 效率是否一样?
    cout<<"右值引用传参"<<endl;
    putRRValue(Copyable(2));
    cout<<"Const 引用传参"<<endl;
    putCLValue(Copyable(2));
    cout<<"----------------------"<<endl;

    //优先调用哪种重载? T&& 还是 const T&?
    whichCall(Copyable(2));
    //这个没什么好纠结的!T&&的出现就是了解决 const T &接受匿名/临时对象后,不能调用非cosnt函数的问题。

    return 0;
}

-----------------------------------------模板函数std::move----------------------------------------

  虽然不能将一个右值引用直接绑定到左值上,但是我们可以显式的将一个左值转换为对应的右值引用类型。我们还可以通过调用一个名为move的新标准库函数来获得绑定到左值上的右值引用,此函数定义在untility中。move函数使用了**机制来返回给定对象的右值引用。

int &&rra = rr1; //error:  //! error!右值引用不能直接绑定到左值
int &&rr = std::move(rr1); // ok!

  move调用告诉编译器:我们有一个左值,但是我们希望像处理一个右值一样去处理他。

  调用move就意味着承诺:除了对rr1赋值或者销毁它之外,我们将不能再使用它。在调用move之后,我们不能对移动后的源对象值做任何的假设。

原文地址:https://www.cnblogs.com/wangkeqin/p/9342093.html

时间: 2024-10-10 21:16:11

移动构造和移动赋值与std::move的相关文章

C++ 11 右值引用以及std::move

转载请注明出处:http://blog.csdn.net/luotuo44/article/details/46779063 新类型: int和int&是什么?都是类型.int是整数类型,int&则是整数引用类型.相同int&&也是一个类型.两个引號&&是C++ 11提出的一个新的引用类型.记住,这是一个新的类型.默念10次吧.假设你记住这个新类型,那么非常多疑问都能迎刃而解.而且对<Effective Modern C++>说到的void f(

C++ 移动语义--std::move 实例解析

移动语义--std::move 编译器只对右值引用才能调用转移构造函数和转移赋值函数,而所有命名对象都只能是左值引用,如果已知一个命名对象不再被使用而想对它调用转移构造函数和转移赋值函数,也就是把一个左值引用当做右值引用来使用,怎么做呢?标准库提供了函数 std::move,这个函数以非常简单的方式将左值引用转换为右值引用. 对于右值引用而言,它本身是右值么? 要看情况. a. 字符串的定义 b. ArrayWrapper String.cc 1 #include<iostream> 2 #i

C++ 11 左值,右值,左值引用,右值引用,std::move, std::foward

这篇文章要介绍的内容和标题一致,关于C++ 11中的这几个特性网上介绍的文章很多,看了一些之后想把几个比较关键的点总结记录一下,文章比较长.给出了很多代码示例,都是编译运行测试过的,希望能用这些帮助理解C++ 11中这些比较重要的特性. 关于左值和右值的定义 左值和右值在C中就存在,不过存在感不高,在C++尤其是C++11中这两个概念比较重要,左值就是有名字的变量(对象),可以被赋值,可以在多条语句中使用,而右值呢,就是临时变量(对象),没有名字,只能在一条语句中出现,不能被赋值. 在 C++1

C++11 std::move 强制转换为右值

[1]std::move 在C++11中,标准库在<utility>中提供了一个有用的函数std::move. 这个函数的名字很具有迷惑性,因为实际上std::move并不能移动任何东西,它唯一的功能:将一个左值强制转化为右值引用,继而可以通过右值引用使用该值,以用于移动语义. 从实现上讲,std::move基本等同于一个类型转换: static_cast<T&&>(lvalue); [2]应用注意项 (1)被std::move转化的左值,其生命期并没有随着转化而改

c++11之右值引用和std::move

这两个特性是c++11里比较有性能提升意义的.个人认为这两个特性也体现了c++对性能提升的极限追求. 通过改写经典c++面试题mystring来体会 move不能减少临时变量的产生,但是可以减少内存的维护量 代码 //右值引用 /* 左值对象:持久存在的对象,具有名字,可以对其去地址 右值对象:临时对象,表达式结束后它就没了,不能对它取地址,它也没有名字~ 右值引用类型:引用右值的类型,用&&来表示 */ /*****************************************

C++ Primer 笔记——理解std::move

标准库move函数是使用右值引用的模板的一个很好的例子.标准库是这样定义std::move的: template <typename T> typename remove_reference<T>::type&& move(T&& t) { return static_cast<typename remove_reference<T>::type&&>(t); } 我们考虑如下代码的工作过程: std::str

C++之拷贝构造与拷贝赋值

拷贝构造和拷贝赋值------一个有点难的问题 介绍之前,我们需要首先了解深拷贝与浅拷贝的差异: 何为深拷贝,深拷贝不会复制指针,而是令目标对象拥有独立的资源,该资源是从元对象中复制,即先找到对象的指针,在通过指针拷贝其内容: 何为浅拷贝,即之赋值指针的地址,不会赋值指针的目标,容易引发double free异常,即多个目标指向同一个内存: 缺省拷贝构造函数和缺省拷贝赋值函数 如果一个类没有显示的定义一个拷贝构造函数和拷贝赋值运算符,则编译器会为其默认提供一个,但是这个函数只能进行浅拷贝: 如果

[转载]如何在C++03中模拟C++11的右值引用std::move特性

本文摘自: http://adamcavendish.is-programmer.com/posts/38190.htm 引言 众所周知,C++11 的新特性中有一个非常重要的特性,那就是 rvalue reference,右值引用. 引入它的一个非常重要的原因是因为在 C++ 中,常常右值,通俗地讲"在等号右边的"临时变量或者临时对象,我们是无法得到它的修改权限的. 由于类的构造和析构机制,往往产生的临时变量或临时对象的拷贝构造及析构,会带来不少的时间.资源消耗. 也同样由于这样的限

c++11 标准库函数 std::move 和 完美转发 std::forward

c++11 标准库函数 std::move 和 完美转发 std::forward #define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <string> #include <vector> #include <map> // C++中还有一个被广泛认同的说法,那就是可以取地址的.有名字的就是左值,反之,不能取地址的.没有名字的就是右值. // 相对于左值,右值表示字面常量.表达式.函数的非