C++ move()函数

目录

1. 左值和右值的概念

2. 引用

3. 左值引用和右值引用

    3.1 左值引用

    3.2 右值引用


1. 左值和右值的概念

        要了解move函数首先弄清左值引用和右值引用。左值、左值引用、右值、右值引用

         左值: 是可以放在赋值号左边可以被赋值的值;左值必须要在内存中有实体;
         右值:当在赋值号右边取出值赋给其他变量的值;右值可以在内存也可以在CPU寄存器。
         :一个对象被用作右值时,使用的是它的内容(值),被当作左值时,使用的是它的地址。

2. 引用

        引用是C++语法做的优化,引用的本质还是靠指针来实现的。引用相当于变量的别名。

        引用可以改变指针的指向,还可以改变指针所指向的值。

        引用的基本规则:

注:声明引用的时候必须初始化,且一旦绑定,不可把引用绑定到其他对象;即引用必须初始化,不能对引用重定义;
对引用的一切操作,就相当于对原对象的操作。


3. 左值引用和右值引用

    3.1 左值引用


         左值引用的基本语法:type &引用名 = 左值表达式;

    3.2 右值引用

        右值引用的基本语法type &&引用名 = 右值表达式;

        右值引用在企业开发人员在代码优化方面会经常用到。

        右值引用的“&&”中间不可以有空格。

std::move并不能移动任何东西,它唯一的功能是将一个左值强制转化为右值引用,继而可以通过右值引用使用该值,以用于移动语义。从实现上讲,std::move基本等同于一个类型转换:static_cast<T&&>(lvalue);
C++ 标准库使用比如vector::push_back 等这类函数时,会对参数的对象进行复制,连数据也会复制.这就会造成对象内存的额外创建, 本来原意是想把参数push_back进去就行了,通过std::move,可以避免不必要的拷贝操作。
std::move是为性能而生。
std::move是将对象的状态或者所有权从一个对象转移到另一个对象,只是转移,没有内存的搬迁或者内存拷贝。
用法:
原lvalue值被moved from之后值被转移,所以为空字符串. 

#include <iostream>
#include <utility>
#include <vector>
#include <string>
int main()
{
    std::string str = "Hello";
    std::vector<std::string> v;
    //调用常规的拷贝构造函数,新建字符数组,拷贝数据
    v.push_back(str);
    std::cout << "After copy, str is \"" << str << "\"\n";
    //调用移动构造函数,掏空str,掏空后,最好不要使用str
    v.push_back(std::move(str));
    std::cout << "After move, str is \"" << str << "\"\n";
    std::cout << "The contents of the vector are \"" << v[0]
                                         << "\", \"" << v[1] << "\"\n";
}

输出:

After copy, str is "Hello"
After move, str is ""
The contents of the vector are "Hello", "Hello"


std::move 的函数原型定义

template <typename T>
typename remove_reference<T>::type&& move(T&& t)
{
    return static_cast<typename remove_reference<T>::type&&>(t);

}

原型定义中的原理实现:
 首先,函数参数T&&是一个指向模板类型参数的右值引用,通过引用折叠,此参数可以与任何类型的实参匹配(可以传递左值或右值,这是std::move主要使用的两种场景)。关于引用折叠如下:

引用折叠在C++模板函数中经常使用,它的规则如下:

  • & + & -> &
  • & + && -> &
  • && + & -> &
  • && + && -> &&

记忆诀窍:凡是折叠中出现左值引用,优先将其折叠为左值引用。

      公式一)X& &、X&& &、X& &&都折叠成X&,用于处理左值

string s("hello");
std::move(s) => std::move(string& &&) => 折叠后 std::move(string& )
此时:T的类型为string&
typename remove_reference<T>::type为string 
整个std::move被实例化如下
string&& move(string& t) //t为左值,移动后不能在使用t
{
    //通过static_cast将string&强制转换为string&&
    return static_cast<string&&>(t); 
}

      公式二)X&& &&折叠成X&&,用于处理右值

std::move(string("hello")) => std::move(string&&)
//此时:T的类型为string 
//     remove_reference<T>::type为string 
//整个std::move被实例如下
string&& move(string&& t) //t为右值
{
    return static_cast<string&&>(t);  //返回一个右值引用
}

简单来说,右值经过T&&传递类型保持不变还是右值,而左值经过T&&变为普通的左值引用.

②对于static_cast<>的使用注意:任何具有明确定义的类型转换,只要不包含底层const,都可以使用static_cast。

double d = 1;
void* p = &d;
double *dp = static_cast<double*> p; //正确
 
const char *cp = "hello";
char *q = static_cast<char*>(cp); //错误:static不能去掉const性质
static_cast<string>(cp); //正确 

③对于remove_reference是通过类模板的部分特例化进行实现的,其实现代码如下

//原始的,最通用的版本
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;  //右值引用特例版本 

总结:
std::move实现,首先,通过右值引用传递模板实现,利用引用折叠原理将右值经过T&&传递类型保持不变还是右值,而左值经过T&&变为普通的左值引用,以保证模板可以传递任意实参,且保持类型不变。然后我们通过static_cast<>进行强制类型转换返回T&&右值引用,而static_cast<T>之所以能使用类型转换,是通过remove_refrence<T>::type模板移除T&&,T&的引用,获取具体类型T。


参考链接:
https://blog.csdn.net/fengbingchun/article/details/52558914 

https://blog.csdn.net/cpriluke/article/details/79462388 

https://blog.csdn.net/swartz_lubel/article/details/59620868
————————————————
版权声明:本文为CSDN博主「ppipp1109」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/p942005405/article/details/84644069
版权声明:本文为CSDN博主「chengjian168」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/chengjian168/article/details/107809308

  • 28
    点赞
  • 110
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
C++ 的 move 函数是一个非常有用的函数,它用于将一个对象的内容移动到另一个对象中,而不是拷贝。通过移动而不是拷贝,可以避免不必要的内存分配和释放,从而提高程序的性能。 move 函数的定义如下: ```cpp template <class T> typename remove_reference<T>::type&& move(T&& arg) noexcept; ``` 其中,`T` 表示要移动的对象的类型。`move` 函数的参数是一个右值引用,这意味着可以将一个临时对象或者一个将要被销毁的对象传递给它。函数返回一个右值引用,表示移动后的对象。 使用 `move` 函数需要注意以下几点: 1. 只有在需要将一个对象的内容移动到另一个对象中时才应该使用 `move` 函数。 2. 移动后的对象可能会变得无效,因此在移动后应该避免对移动前的对象进行操作。 3. 对于内置类型和标准库类型,移动函数已经被正确实现,无需自己实现。 下面是一个使用 `move` 函数的例子: ```cpp #include <iostream> #include <vector> #include <string> using namespace std; int main() { vector<string> vec1{"Hello", "World"}; // 创建一个 vector 对象 vector<string> vec2{move(vec1)}; // 移动 vec1 的内容到 vec2 中 cout << vec1.size() << endl; // 输出 0,因为 vec1 已经被移动了 cout << vec2.size() << endl; // 输出 2,因为 vec2 中有两个元素 return 0; } ``` 在上面的例子中,我们创建了一个 vector 对象 `vec1`,然后将它的内容移动到另一个 vector 对象 `vec2` 中。由于移动后 `vec1` 变得无效,因此在输出 `vec1.size()` 时会得到 0。而 `vec2` 中有两个元素,因此输出 `vec2.size()` 时会得到 2。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值