zoukankan      html  css  js  c++  java
  • C11性能之道:转移和转发

    1、move

      C++11中可以将左值强制转换为右值,从而避免对象的拷贝来提升性能。move将对象的状态或者所有权从一个对象转移到另一个对象,没有内存拷贝。深拷贝和move的区别如图:

      从图可以看出,深拷贝会有两份内存,而move只有一份,move只是将内存的所有者切换为目标对象,并没有移动任何东西,只是强制将左值转换为右值。

      在C++11之前的拷贝构造函数和赋值函数要有如下定义:

    //赋值函数
    T &T::operator=(const T& rhs)
    {
        //销毁内部资源
        //复制rhs资源到自身
    }
    
    //拷贝函数
    A foo(); //假如foo是返回一个A类型的函数
    
    A a;
    a = foo();

      a = foo()将有如下动作:

    1. 销毁a的资源;
    2. 赋值foo返回的临时对象的资源;
    3. 销毁临时对象,释放其资源。

      其实可以优化的是,将a和临时对象的资源指针做交换,让临时对象去销毁a原来拥有的资源,a拥有临时对象的资源指针。那么赋值操作函数就该这么写:

    T &T::operator=(const T& rhs)
    {
        //转移资源的控制权,无需复制
    }

      假如一个临时容器很大,要赋值给另一个容器:

    //常规
    std::list<std::string> tokens; //很大容量
    std::list<std::string> ret = tokens;
    
    //move
    std::list<std::string> tokens; //很大容量
    std::list<std::string> ret = std::move(tokens);

      如果不用move,拷贝的代价很大,性能较低,使用move几乎没有代价,只转换了资源的所有权,实际上将左值变成右值引用。move对于拥有内存、文件句柄等资源的对象有效,但是对于int、char等类型仍然会产生拷贝。当然,当一个左值被move之后,不再对之前的内存有控制权,会被置为一个空对象。

    #include <iostream>
    
    using namespace std;
    
    int main()
    {
        string s = "hello";
        cout << "before:" << s << endl;
        
        string s2 = move(s);
        cout << "after:" << s << endl;
        cout << "s2:" << s2 << endl;
        
        return 0;
    }
    
    //运行结果
    before:hello
    after:
    s2:hello

    2、forward完美转发

      之前的文章有提到过,当一个右值经过函数内部的转发会将其类型变成一个左值,并不是他原来的类型。

    foo(int && i)
    {
        foo2(i);
    }
    
    foo(5);  //5在此时是右值,在foo2调用的时候就是左值了

      那么我们该怎么优化,使参数按照原来的类型来转发呢?C++11提供了std::forward,将参数传递给函数中调用的另一个函数,按照参数本来的类型转发。

      具体用法如下:

    #include <iostream>
    #include <utility>
    
    using namespace std;
    
    void PrintValue(int &i)
    {
        cout << "Lvalue:" << i << endl;
    }
    
    void PrintValue(int &&i)
    {
        cout << "Rvalue:" <<  i << endl;
    }
    
    void Forward(int &&i)
    {
        cout << "no forward:";
        PrintValue(i);                     //未经转发会变成左值
        
        cout << "forward:";
        PrintValue(std::forward<int>(i)); //转发
    }
    
    int main()
    {
        int i = 1;
        
        cout << "param Rvalue:" << endl;
        Forward(2);
        
        //cout << "param Lvalue:" << endl;
        //Forward(i);              //不能传入左值
        
        cout << "move Lvalue:" << endl;
        Forward(move(i));
    
        return 0;
    }
  • 相关阅读:
    WinDbg调试C#技巧,解决CPU过高、死锁、内存爆满
    Window环境下搭建Git服务器
    Virtual Box虚拟机Ubuntu系统安装及基本配置
    修改VS2017新建类模板文件添加注释
    .net core 使用IIS作为宿主Web服务器,部署常见问题
    Asp.Net进程外Session(状态服务器Session、数据库Session)
    百度地图DEMO-路线导航,测距,标点
    c#文件图片操作
    C#代码安装Windows服务(控制台应用集成Windows服务)
    通过经纬度获取地址信息
  • 原文地址:https://www.cnblogs.com/ChinaHook/p/7684204.html
Copyright © 2011-2022 走看看