目录
概念
提到Move引出一个C++ 中有左值和右值的概念,简单来说,左值指的是可以取地址的表达式,右值指的是不可以取地址的表达式。
用途:
左值引用通常用于函数参数传递和返回值类型定义,能够实现引用传递,在函数中可以改变引用对象的值。右值引用则主要用于移动语义和完美转发。
移动语义是指将右值的资源所有权转移给其它对象的能力,可以避免拷贝构造函数的性能问题。完美转发是指在传递函数参数时,将实参的左值引用或右值引用保持不变地传递给下一个函数,可以避免实参值的不必要拷贝
示例
int a = 10; // a为左值
int& b = a; // b为左值引用
int&& c = 10; // c为右值引用
std::Move:template typename remove_reference::type&& move(T&& arg) noexcept;
简单说就是说改变内存所属权,比如a=10;使用move(b);那么b=10;a=0或者编译器处理。Move最主要的作用是实现移动语义,避免不必要的复制操作,move函数的参数是一个通用引用(universal reference),既可以接受左值类型,也可以接受右值类型
用例示例
#include <stdio.h>
#include <unistd.h>
#include <iostream>
#include <vector>
class MyClass {
public:
MyClass(int value)
:ptr_(new int(value)) { // 构造函数,存在开辟内存、复制资源的操作
std::cout << "Default constructor called: MyClass(int value)" << std::endl;
}
MyClass(const MyClass& other) // 拷贝构造函数,存在开辟内存、复制资源的操作
: ptr_(new int(*other.ptr_)) {
std::cout << "Copy constructor called: MyClass(const MyClass& other)" << std::endl;
}
MyClass(MyClass&& other) noexcept // 移动构造函数,只是地址的复制,没有新开内存、资源复制
: ptr_(other.ptr_) {
other.ptr_ = nullptr;
std::cout << "Move constructor called: MyClass(MyClass&& other)" << std::endl;
}
MyClass& operator=(const MyClass& other) { // 赋值构造函数,也存在开辟内存、复制资源的操作
if (&other == this) {
return *this; // 自我赋值,直接返回
}
if (ptr_) {
delete ptr_; // 释放原有内存
}
// 逐个赋值
ptr_ = new int(*other.ptr_);
return *this;
}
~MyClass() {
if (ptr_) {
delete ptr_;
}
std::cout << "Destructor called." << std::endl;
}
int GetValue(void) { return *ptr_; }
// 打印数据
void PrintData() const {
std::cout << "Data: " << *ptr_ << std::endl;
}
private:
int* ptr_; // 相当于Class内部管理的资源
};
int main (void)
MyClass obj1(10); // 调用默认构造函数
MyClass obj2 = std::move(obj1); // 调用移动构造函数
return 0;
}
原型:
std::move是C++11新增加的一个函数模板,其主要功能是将一个左值强制转换为一个右值引用。
其原型定义如下:
template<typename T>
typename remove_reference<T>::type&& move(T&& arg) noexcept;其中remove_reference是一个类型转换模板,其主要功能是去除模板类型T的引用类型,返回一个非引用类型。
函数move接收一个泛型参数,该参数被声明为一个右值引用,也就是T&&。当函数move接收到一个左值参数时,其会将该参数强制转换为一个右值引用,并返回一个右值引用类型的转换结果。
在C++11中,移动语义的引入解决了传递复制构造函数和复制赋值运算符中的性能问题。当使用std::move函数将一个左值转换为右值引用时,可以避免不必要的对象复制和内存分配,从而提高程序的性能和效率。
从本质上讲,我们可以将std::move视为一个左值==》右值的类型转换:
static_cast<T&&>(lvalue)
首先,函数参数T&&是一个指向模板类型参数的右值引用,通过引用折叠,此参数可以与任何类型的实参匹配(可以传递左值或右值,这是std::move主要使用的两种场景)。
//原始的,最通用的版本
template <typename T> struct remove_reference{
typedef T type; //定义T的类型别名为type
};
//部分版本特例化,将用于左值引用和右值引用
template <class T> struct remove_reference<T&> //左值引用
{ typedef T type; }
template <class T> struct remove_reference<T&&> //右值引用
{ typedef T type; }
//举例如下,下列定义的a、b、c三个变量都是int类型
int i;
remove_refrence<decltype(42)>::type a; //使用原版本,
remove_refrence<decltype(i)>::type b; //左值引用特例版本
remove_refrence<decltype(std::move(i))>::type b; //右值引用特例版本
1、首先,通过右值引用传递模板实现,利用引用折叠原理将右值经过
T&&
传递类型保持不变还是右值,而左值经过T&&
变为普通的左值引用,以保证模板可以传递任意实参,且保持类型不变。(cpr:先能够把参数类型全都接收)2、然后我们通过static_cast<>进行强制类型转换返回
T&&
右值引用,而static_cast之所以能使用类型转换,是通过remove_refrence::type模板移除T&&,T&的引用,获取具体类型T(模板偏特化)。(cpr:再把接收的参数的原引用抹除强转成右值引用)