切记所有代码需要在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;
∅
//函数也是左值
&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);
}