zoukankan      html  css  js  c++  java
  • 二十分钟弄懂C++11 的 rvalue reference (C++ 性能剖析 (5))

    C++ 11加了许多新的功能。其中对C++性能和我们设计classconstructorassignment可能产生重大影响的非rvalue reference莫属!我看了不少资料,能说清它的不多。下面我企图用简单的例子来说明,希望读者能够理解并应用这一重要的语言构造。

    1.rvalue reference 是reference (即指针)

    比如下面两条语句的语义完全一样:

    int &&p = 3;             // line 1

    const int &cp = 3      // line 2

    2. rvalue reference 指向临时变量

    上面的line1line2的共同点是,他们都指向临时变量。所不同的是下面两句:

    p = 5;       // p 的内容变成了5

    cp = 5;      // 编译出错:cp 不能改动(常数) 

    3.rvalue reference可以简化moving 语义 – 提高object 拷贝性能

    很好,我们现在可以通过rvalue reference修改(阴暗中的)临时变量了。那么这有什么用呢?目前C++11所宣称的最主要的应用就是所谓的“moving semantics (迁移语义)”。请看下面例子:

    class SimpleString

    {

           char * _ptr;

    public:

           SimpleString(const char *p);

           SimpleString(const SimpleString & another);

           ~SimpleString();

           operator const char * () { return _ptr; }

           SimpleString & operator = (const SimpleString & another);

           static void Test();

    private:

           void GetStr(const char *p);

    };

    SimpleString::SimpleString(const char *p): _ptr(nullptr)

    {

           GetStr(p);

    }

    SimpleString::SimpleString(const SimpleString & another): _ptr(nullptr)

    {

           GetStr(another._ptr);

    }

    void SimpleString::GetStr(const char *p)

    {

           if (_ptr)

                 delete [] _ptr;

     

           size_t l= ::strlen(p);

           _ptr = new char[l+1];

           ::strcpy_s(this->_ptr, l+1, p);

    } 

    SimpleString::~SimpleString()

    {

           if (_ptr)

           {

                 delete [] _ptr;

                 printf("SimpleString d'tr called for ");

           }

    }

    SimpleString & SimpleString::operator = ( const SimpleString & another)

    {

           GetStr(another._ptr);

           return *this;

    }

    namespace

    {

          // simple string factory

           SimpleString CreateString()

           {

                 SimpleString temp("A temp string created!");

                 return temp;

           }

    } 

    void SimpleString::Test()

    {

           SimpleString ret = CreateString();

           printf("ret is: &s ", ret);

    }

    上面是一个为了试验用的简单string class。 假设我们有一个函数CreateString, 返回一个创建的SimpleString 值。然后赋给接受变量ret。 这个简单的逻辑有什么问题呢?

    这里就是临时变量copy constructor的问题。我们这里用了SimpleString::SimpleString(const SimpleString & another),它用GetStr来构建一个新的指针_ptr。然后将临时变量的_ptr所指内容拷贝过来。

    这是常见的做法,但是很昂贵的。CreateString函数已经构建了一个有效的_ptr,为什不能拷贝指针呢?

    原来,因为CreateString里的temp变量是临时变量,它在CreateString出口时将会被销毁,除非我们能获取他的referencepointer,然后将它的_ptr设为null。这是个好主意,我们再加一个函数: 

    void SimpleString::MoveStr(SimpleString & another)

    {

           if (this->_ptr)

                 delete this->_ptr;

     

           this->_ptr = another._ptr;

           another._ptr = nullptr;

    }

    然后把copy constructor 改写:

    SimpleString::SimpleString(const SimpleString & another): _ptr(nullptr)

    {

           MoveStr(const_cast<SimpleString &>(another)); // line 100

    }

    这样一来,我们就只构建一次_ptr了,测试的结果也证明了这一点。

    上面讲的和rvalue reference有何关系呢?

    我对line 100的方案不太满意:

    1) 我们改变了原来copy constructor的常规意义,现在只要你赋值与另一变量,你就失去了你自己的值。我们希望这个功能只适合于“临时变量”。

    2) const_cast 不太好,不美观。

    现在,我们因该悟出rvalue reference的意义了吧?

    根据第二节,rvalue reference是指向临时变量的,正好是用于指向CreateString产生的临时变量。

    原来,只要我们在SimpleString里加一个moving copy constructor(注意&&): 

    SimpleString::SimpleString(SimpleString && another): _ptr(nullptr)

    {

           MoveStr(another);

    }

    我们便无需更改SimpleString::SimpleString(const SimpleString & another)了。C++编译自动地在这一行SimpleString ret = CreateString() call 我们的moving constructor SimpleString::SimpleString(SimpleString && another), 而不是我们的copy constructor.

    大家不妨试试!

    总结

    C++11利用rvalue reference,使我们可以方便地实现 moving constructor 语义。这对上述类似的问题(特别是std里的container用法)提供了解决C++传统的临时变量拷贝的功能隐患。

    附录:修改后的代码

    // header: RValueRef.h

    class SimpleString

    {

             char * _ptr;

    public:

             SimpleString(const char *p);

             SimpleString(const SimpleString & another);

             SimpleString(SimpleString && another); // moving constructor

     

             ~SimpleString();

             operator const char * () { return _ptr; }

             SimpleString & operator = (const SimpleString & another);

             SimpleString & SimpleString::operator = ( SimpleString && another);

     

             static void Test();

     

    private:

             void GetStr(const char *p);

             void MoveStr(SimpleString & another);

    };

    // C++: rvalue.cpp

    #include "stdafx.h"

    #include <string.h>

    #include <stdlib.h>

    #include <stdio.h>

    #include <errno.h>

    #include "RValueRef.h"

     

    SimpleString::SimpleString(const char *p): _ptr(nullptr)

    {

             GetStr(p);

    }

     

    SimpleString::SimpleString(const SimpleString & another): _ptr(nullptr)

    {

             GetStr(another._ptr);

    }

     

    // Moving constructor helps move temp var’s _ptr to ourselves.

    SimpleString::SimpleString(SimpleString && another): _ptr(nullptr)

    {

             MoveStr(another);

    }

    void SimpleString::GetStr(const char *p)

    {

             if (_ptr)

                     delete [] _ptr;

     

             size_t l= ::strlen(p);

             _ptr = new char[l+1];

             ::strcpy_s(this->_ptr, l+1, p);

    }

    SimpleString::~SimpleString()

    {

             if (_ptr)

             {

                     printf("SimpleString d'tr called for '%s' ", _ptr);

                     delete [] _ptr;

             }

    }

    SimpleString & SimpleString::operator = ( const SimpleString & another)

    {

             GetStr(another._ptr);

             return *this;

    }

    SimpleString & SimpleString::operator = ( SimpleString && another)

    {

             MoveStr(another);

             return *this;

    }

    void SimpleString::MoveStr(SimpleString & another)

    {

             if (this->_ptr)

                     delete this->_ptr;

     

             this->_ptr = another._ptr;

             another._ptr = nullptr; // don’t forget to do this

    }

    namespace

    {

              SimpleString CreateString()

             {

                     SimpleString temp("A temp string created!");

                     return temp;

             }

     

    }

    void SimpleString::Test()

    {

             SimpleString ret = CreateString();

             printf("ret is: &s ", ret);

    }

  • 相关阅读:
    gorilla/mux 的学习
    SwitchyOmega 配置
    golang []byte 和 string相互转换
    golang 并发demo 写入 redis
    VS2010 显示TFS删除项
    WPF之转换器
    DataTemplate的用法
    DynamicResource与StaticResource的区别
    除非Windows Activation Service (WAS)和万维网发布服务(W3SVC)均处于运行状态,否则无法启动网站。
    WPF 可视化树的用途
  • 原文地址:https://www.cnblogs.com/ly8838/p/3957175.html
Copyright © 2011-2022 走看看