C++11/14标准(一)左值与右值

切记所有代码需要在C++11标准下运行,如果使用g++,请添加-std=c++11。

  • 左值和右值

    左值是一个用来存储数据的变量,有自己的内存空间,表达式结束后仍然存在。右值是一个临时变量,它在表达式结束时生命周期终止。

通俗的说法就是能使用取地址符的值就是左值,不能使用取地址符的值就是右值。

    左值可以是函数或者对象。

///
 /// @file    lvalue.cpp
 /// @author  kogan([email protected])
 /// @date    2020-06-16 09:11:18
 ///
 
#include <iostream>
using std::cout;
using std::endl;

class Empty
{
//面试中常会问的问题,空类占一个字节。因为空类实例化之后就会拥有独一无二的内存地址
};

void TestFunc(void);

int main()
{
  int variable = 5;
  //int型变量取地址。取地址后,&variable是右值。
  &variable;

  //&&variable; //编译会报错,所以注释。

  //类也是左值
  Empty empty;
  &empty;

  //函数也是左值
  &TestFunc;  
  return 0;
}

void TestFunc(void)
{
}
  • 右值引用

    使用T&&的形式表示右值引用。T&为左值引用。

///
 /// @file    llvalue.cpp
 /// @author  kogan([email protected])
 /// @date    2020-06-17 10:12:38
 ///
 
#include <iostream>
using std::cout;
using std::endl;

int main()
{
    
  int x = 0;
  //左值引用
  int &r1 = ++x;
  //右值引用
  int &&r2 = x++;
  //常亮左值引用
  const int &r3 = x++;
    //此时r2延长了x++返回的值的生命周期。
  cout << r2 << endl;
  return 0;
}

    这里提及一下,前置++和后置++。先说结论前置++效率更高。前置++返回一个左值。后置++返回一个右值。


前置++本质是:
self& operator++()
{  
  ++current; 
  return(*this);
}

后置++本质是:
self operator++(int)
{  
  self tmp = *this;  
  ++(*this);  
  return tmp;
}

self是指对象类型。后置++需要创建一个临时对象,然后将原来对象的值赋值给临时对象。然后对象自增,返回临时对象。同时为了区分前置++和后置++,后置++中函数参数有一个0。这也就是后置++中带有一个int参数。

    

    代码如下:


///
 /// @file    prefix_and_suffixpp.cpp
 /// @author  kogan([email protected])
 /// @date    2020-06-17 09:24:10
 ///
 
#include <iostream>
using std::cout;
using std::endl;

class Int
{
public:
  Int():m_variable(0)
  {
  }

  int operator++(int i)
  {  
    cout << "suffix ++ "  << endl;
    int tmp = (this->m_variable);  
    ++(this->m_variable);  
    return tmp;
  }
    
  int& operator++()
  {  
    cout << "prefix ++ "  << endl;
    ++(this->m_variable); 
    return((this->m_variable));
  }
private:
  int m_variable;
};

int main()
{
  Int i;
  
  cout << "---------------i++-------------------" << endl;
  cout << "i = " << i++ << endl;
  cout << "---------------++i-------------------" << endl;
  cout << "i = " << ++i << endl;
  
  return 0;
}

运行结果为:

  • 移动语义

    引用折叠(reference collapsing):对左值引用或者右值引用再进行左值引用是T&,右值引用不变化。函数参数使用T&&将保持原型不变。

    此时需要介绍std::move()函数。

    move()声明:

template <class T>
typename remove_reference<T>::type && move(T&& t) noexcept;
//等价于
static_cast<T &&> t;

该函数主要作用是将函数参数转换成匿名的右值引用(T&&)。

    C++11/14中为class增加了参数类型为T&&的转移构造函数和转移赋值函数,只要类实现了这两个特殊函数就能利用右值对象进行零成本构造。

    

 ///
 /// @file    moveable.cpp
 /// @author  kogan([email protected])
 /// @date    2020-06-17 10:56:47
 ///
 
#include <iostream>
#include <utility>
using std::cout;
using std::endl;

class moveable
{
public:
  moveable () = default;
  moveable (moveable&& other)
  {
    cout << "moveable destructor &&" << endl;
    std::swap(x, other.x);
  }

  moveable& operator = (moveable&& other)
  {
    std::swap(x, other.x);
    return *this;
  }
  
    static moveable create()
{
    moveable obj;
    return obj;
  }
private:
  int x;
};

int main()
{
  moveable m1;    //缺省构造函数
  moveable m2(std::move(m1));  //调用转移构造函数,m1被转移
  moveable m3 = moveable::create();  //调用转移赋值函数

  return 0;
}

C++标准库中string、vector、deque等组件都实现了转移构造函数和转移赋值函数。

emplace()、emplace_back()使用右值插入元素。

  • 完美转发

    forward(),在泛型编程时转发,可以把函数参数原封不动地转发给其他函数。

    template<class T> T&& forward(T&& t) noexcept;


void check(int &&)
{
  cout << "rvalue" << endl;
}

template<typename T>
void print(T&& v)
{
  check(std::forward<T> (v);
 }

猜你喜欢

转载自blog.csdn.net/koganlee/article/details/106873322
今日推荐