注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

还东国的博客

行之苟有恒,久久自芬芳

 
 
 

日志

 
 

C++11及BOOST特性之八移动构造和std::move  

2014-04-09 14:47:49|  分类: C++(VC)编程 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

C++11BOOST特性之八移动构造和std::move

在前面“C++11BOOST特性之六智能指针和std::move”中已经提到过了std::move,但是在C++11C++1X,现在VS2013基本是在向C++14看齐)中提出了移动构造这个概念。那么这两者有什么关系呢?

下面先看几个例子,理解一下左值和右值:

#include <iostream>

#include <ostream>

int GetDataInt()

{

         return 10;

}

std::string GetName()

{

         std::string name = "name";

         return name;

}

void TestErrorString2(int& s)

{

         return;

}

void TestErrorString(const std::string& s)

{

         std::string s1 = "tttt";

          std::cout << "12345"<< std::endl;

          std::cout << s1.c_str() << std::endl;

}

void TestErrorString2(int &&s)

{

         //std::cout << s.c_str() << std::endl;

}

int _tmain(int argc, _TCHAR* argv[])

{

         //测试右值引用

         //int &gt = GetDataInt();//非常量的初始化必须为左值

         int && gt = GetDataInt();//右值引用成功

         const int &gtt = GetDataInt();

 

         std::string sname = "sname";

         TestErrorString2(GetDataInt());

         TestErrorString(GetName());

}

可以注意到非常量的引用临时变量(函数返回值)会报一个错误“非常量的初始化必须为左值”,这种情况一般用到的情况还是比较少见的,更多得是下面的TestErrorString2这种情况,如果没有右值引用的重载那种情况,程序会报一个错误,实际同上(可能会报无法从参数 1 从“int”转换为“int &”,但实质是一样的)。

那这样就清楚了,可是如果引用到一个类里,构造函数时传入一个右值会怎么样呢?也就是传统的拷贝构造函数:(这个例子参考了网上的例子)

class TestConstruct

{

public:

    // 默认构造函数

    TestConstruct (int n)

        : _pData ( new int[ n ] )

        , _size( n )

    {}

 

    // copy constructor

    TestConstruct (const TestConstruct & other)

        : _pData ( new int[ other._size  ] )

        , _size( other._size )

    {

           Memcpy(_pData,other._pData,other._size);

    }

    ~ TestConstruct ()

    {

        delete [] _pData;

    }

 

private:

    int *_pData;

    int _size;

};

然后发现如果传进去一上右值,地址空间的复制和创建销毁是有很大的代价的。不如利用起来,于是就可以使用右值构造函数:

class TestConstruct

{

public:

    // 默认构造函数

    TestConstruct ()

        : _pData ( new int[ 64 ] )

        , _size( 64 )

    {}

 

    TestConstruct (int n)

        : _pData ( new int[ n ] )

        , _size( n )

    {}

 

    // move constructor

    TestConstruct (TestConstruct && other)

        : _pData ( other. _pData  )

        , _size( other._size )

    {

        other. _pData = NULL;

    }

 

    // copy constructor

    TestConstruct (const TestConstruct & other)

        : _pData ( new int[ other._size  ] )

        , _size( other._size )

    {

           Memcpy(_pData,other._pData,other._size);

    }

    ~ TestConstruct ()

    {

        delete [] _pData;

    }

 

private:

    int *_pData;

    int _size;

};

 

既然编译器只对右值引用才能调用转移构造函数和转移赋值函数,而所有命名对象都只能是左值引用,如果已知一个命名对象不再被使用而想对它调用转移构造函数和转移赋值函数,也就是把一个左值引用当做右值引用来使用,怎么做呢?标准库提供了函数 std::move,这个函数以非常简单的方式将左值引用转换为右值引用

示例程序 :

 void ProcessValue(int& i) {

  std::cout << "LValue processed: " << i << std::endl;

 }

 

 void ProcessValue(int&& i) {

  std::cout << "RValue processed: " << i << std::endl;

 }

 

 int main() {

  int a = 0;

  ProcessValue(a);

  ProcessValue(std::move(a));

 }

运行结果 :

 LValue processed: 0

 RValue processed: 0

std::move在提高 swap 函数的的性能上非常有帮助,一般来说,swap函数的通用定义如下:

    template <class T> swap(T& a, T& b)

    {

        T tmp(a);   // copy a to tmp

        a = b;      // copy b to a

        b = tmp;    // copy tmp to b

 }

有了 std::moveswap 函数的定义变为 :

    template <class T> swap(T& a, T& b)

    {

        T tmp(std::move(a)); // move a to tmp

        a = std::move(b);    // move b to a

        b = std::move(tmp);  // move tmp to b

 }

通过 std::move,一个简单的 swap 函数就避免了 3 次不必要的拷贝操作。

Std::move肯定是为效率而生,但是转移构造函数却不一定会,看应用的场景,这里不再细展开,以后有机会再慢慢的说。

  评论这张
 
阅读(603)| 评论(0)
推荐 转载

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017