C++ 移动语义--std::move 实例解析

移动语义--std::move

  1. 编译器只对右值引用才能调用转移构造函数和转移赋值函数,而所有命名对象都只能是左值引用,如果已知一个命名对象不再被使用而想对它调用转移构造函数和转移赋值函数,也就是把一个左值引用当做右值引用来使用,怎么做呢?标准库提供了函数 std::move,这个函数以非常简单的方式将左值引用转换为右值引用。
  2. 对于右值引用而言,它本身是右值么? 要看情况。

      a. 字符串的定义

      b. ArrayWrapper

String.cc

  1 #include<iostream>
  2 #include<string.h>
  3
  4 class String
  5 {
  6 public:
  7     String()
  8     {
  9         std::cout << “String()” << std::endl;
 10         pstr_ = new char[1];
 11         }
 12
 13         String(const char *pstr)
 14         {
 15         std::cout << “String(const char *pstr)” << std::endl;
 16         pstr_ = new char[strlen(pstr) + 1];
 17         strcpy(pstr_ , pstr);
 18         }
 19
 20         String(const String &rhs)    //复制构造函数
 21         {
 22         std::cout << “String(const String &rhs)” << std::endl;
 23         pstr_ = new char[strlen(rhs.pstr_) + 1];
 24         strcpy(pstr_, rhs.pstr_);
 25         }
 26
 27         String(String &&rhs)   //移动构造函数,移动就意味着修改,所以不加const。当发现是右值时,会优先绑定移动构造函数。
 28         : pstr_(rhs.pstr_)
 29         {
 30         std::cout << “String(String &&rhs)” << std::endl;
 31         rhs.pstr_ = NULL;
 32         }
 33
 34         String &operator=(String &&rhs)    //移动赋值运算符函数
 35         {
 36         std::cout << “String &operator=(String &&rhs)” << std::endl;
 37         if(this != &rhs)
 38         {
 39             delete []pstr_;
 40             pstr_ = rhs.pstr_;
 41             rhs.pstr_ = NULL;
 42             }
 43             return *this;
 44         }
 45
 46         String &operator=(const String &rhs)   //赋值运算符函数
 47         {
 48         std::cout << “String &operator=(const String &rhs)” << std::endl;
 49         if(this != &rhs)
 50         {
 51         delete []pstr_;
 52         pstr_ = new char[strlen(rhs.pstr_) + 1];
 53         strcpy(pstr_ , rhs.pstr_);
 54             }
 55             return *this;
 56         }
 57
 58         String & operator += (const String &rhs)
 59         {
 60         std::cout << “String & operator+=(const String &rhs) ”  <<std::cout;
 61         int len = strlen(pstr_) + strlen(rhs.pstr_) + 1;
 62         char * ptmp = new char[len+1];
 63         strcpy(ptmp, pstr_);
 64         strcat(ptmp, rhs.pstr_);
 65
 66         return *this;
 67         }
 68
 69         ~String()
 70         {
 71         if(pstr_)
 72         std::cout << “~String() pstr_ = ” << std::hex << reinte << std::endl;
 73         else
 74         std::cout << “~String() pstr_ = NULL”  << std::endl;
 75         delete []pstr_;
 76          }
 77
 78          friend std::ostream &operator<<(std::ostream &os, const String &rhs);
 79 private:
 80     char *pstr_;
 81 };
 82
 83 String operator + (const String &lhs, const String &rhs)
 84 {
 85     std::cout << “String operator + (const String &lhs, const String &rhs)” <<std::endl;
 86     String tmp(lhs);
 87     tmp += rhs;
 88
 89     return tmp;
 90 }
 91
 92 String operator+(const String &lhs, const char *rhs)
 93 {
 94     std::cout << “String operator + (const String &lhs, const char *rhs)” <<std::endl;
 95     String tmp(rhs);
 96     tmp += lhs;
 97
 98     return tmp;
 99 }
100
101 String operator+(const char *lhs, const String &rhs)
102 {
103     std::cout << “String operator + (const char *lhs, const String &rhs)” <<std::endl;
104     String tmp(lhs);
105     tmp += rhs;
106
107     return tmp;
108 }
109
110 std::ostream &operator<<(std::ostream &os, const String &rhs)
111 {
112     os<<rhs.pstr_;
113     return os;
114 }
115
116 String getStr()
117 {
118     std::cout << “==========” <<std::endl;
119         String s1 = “hello”;
120         return s1;
121 }
122
123 int main(void)
124 {
125     String s1 = “hello world”;    //调用有参构造函数, 还会优先调用移动构造函数
126     //hello world 通过隐式转换会生成一个String的临时对象,这个临时对象就是一个右值, 右值会优先绑定到右值引用,调用 移动构造函数,而不是去调用 复制构造函数。如果没有 移动构造函数, 就会调用 复制构造函数 ,因为复制构造函数 的参数是 常量左值引用,它可以绑定到所有类型的值,包括 非常量左值、常量左值、右值。
127
128     std::cout << “移动构造函数” << std::endl;
129     getStr();
130     std::cout << “-------------” << std::endl;
131
132     String s3 = s1 + s2;  // 会调用 构造函数、赋值运算符函数、+=运算符函数 、 移动赋值运算符函数 和 析构函数, 优先调用移动赋值运算符函数的原因是:发生了右值赋值给了左值。
133
134     return 0;
135 }            

Std_move.cc

 1 #include<iostream>
 2 #include<string>
 3
 4 class MetaData
 5 {
 6 public:
 7     MetaData(int size, const std::string &name)
 8         :size_(size), name_(name)
 9     {
10         std::cout << “MetaData(int size, const std::string &name)” <<std::endl;
11 }
12
13     MetaData(const MetaData &other)
14         :name_(other.name_), size_(other.size_)
15     {
16         std::cout << “MetaData(const MetaData &other)” <<std::endl;
17 }
18
19     MetaData(MetaData &&other)  //移动构造函数 , 不加const
20         //: name_(other.name_)   // std::string 的复制构造函数
21         : name_(std::move(other.name_)), size_(other.size_)
22     {
23         std::cout << “MetaData(MetaData &&other)” <<std::endl;
24 }
25
26 std::String getName() const
27 {
28     return name_;
29 }
30
31 int getSize() const
32 {
33     return size_;
34 }
35
36 private:
37     std::string name_;
38     int size_;
39 };
40
41 class ArrayWrapper
42 {
43 public:
44     ArrayWrapper()
45         : pVals_(new int[64]), metadata_(64, “ArrayWrapper”)
46     {
47         std::cout << “ArrayWrapper()” << std::endl;
48         }
49
50     ArrayWrapper(int n)
51         : pVals_(new int[n]), metadata_(n, “ArrayWrapper”)
52     {
53         std::cout << “ArrayWrapper(int n)” << std::endl;
54         }
55
56     ArrayWrapper(const ArrayWrapper &other)
57         : pVals_(new int[other.metadata_.getSize()]), metadata_(other.metadata_)
58     {
59         std::cout << “ArrayWrapper(const ArrayWrapper &other)”  << std::endl;
60         for(inti dx = 0; idx != other.metadata_.size_; ++idx)
61         {
62             pVals_[idx] = other.pVals_[idx];
63                 }
64         }
65
66         ArrayWrapper (ArrayWrapper &&other)    //移动构造函数 , 不加const
67          : pVals_(other.pVals_),
68 //metadata_(other.metadata_)   //other.metadata_本身是一个左值,会调用Metadata类的复制构造函数,与这里的初衷有相违背的地方,外层是移动构造,内层也得是移动构造,这种语义才算正常,要不然不是真正的移动构造语义。所以要换成 移动构造。
69              metadata_(std::move(other.metadata_))  //这里会调用Metadata的移动构造函数。
70         {
71     std::cout << “ArrayWrapper(ArrayWrapper &&other)”  << std::endl;
72     other.pVals_ = NULL;
73         }
74
75          ~ ArrayWrapper()
76         {
77         std::cout << “~ArrayWrapper()” << std::endl;
78         delete []pVals_;
79         }
80
81 private:
82     int *pVals_;
83     MetaData metadata_;
84 };
85
86 int main(void)
87 {
88     ArrayWrapper aw1;
89     std::cout << “移动构造函数” << std::endl;
90     ArrayWrapper aw2(std::move(aw1));   //使用std::move将aw1变成一个右值应用,传给移动构造函数。
91
92     return 0;
93 }
时间: 2024-09-28 17:14:17

C++ 移动语义--std::move 实例解析的相关文章

C++0x,std::move和std::forward解析

1.std::move 1.1std::move是如何定义的 template<typename _Tp> constexpr typename std::remove_reference<_Tp>::type&& move(_Tp&& __t) noexcept { return static_cast<typename std::remove_reference<_Tp>::type&&>(__t); }

C++ 11 左值,右值,左值引用,右值引用,std::move, std::foward

这篇文章要介绍的内容和标题一致,关于C++ 11中的这几个特性网上介绍的文章很多,看了一些之后想把几个比较关键的点总结记录一下,文章比较长.给出了很多代码示例,都是编译运行测试过的,希望能用这些帮助理解C++ 11中这些比较重要的特性. 关于左值和右值的定义 左值和右值在C中就存在,不过存在感不高,在C++尤其是C++11中这两个概念比较重要,左值就是有名字的变量(对象),可以被赋值,可以在多条语句中使用,而右值呢,就是临时变量(对象),没有名字,只能在一条语句中出现,不能被赋值. 在 C++1

c++11之右值引用和std::move

这两个特性是c++11里比较有性能提升意义的.个人认为这两个特性也体现了c++对性能提升的极限追求. 通过改写经典c++面试题mystring来体会 move不能减少临时变量的产生,但是可以减少内存的维护量 代码 //右值引用 /* 左值对象:持久存在的对象,具有名字,可以对其去地址 右值对象:临时对象,表达式结束后它就没了,不能对它取地址,它也没有名字~ 右值引用类型:引用右值的类型,用&&来表示 */ /*****************************************

C++ 11 右值引用以及std::move

转载请注明出处:http://blog.csdn.net/luotuo44/article/details/46779063 新类型: int和int&是什么?都是类型.int是整数类型,int&则是整数引用类型.相同int&&也是一个类型.两个引號&&是C++ 11提出的一个新的引用类型.记住,这是一个新的类型.默念10次吧.假设你记住这个新类型,那么非常多疑问都能迎刃而解.而且对<Effective Modern C++>说到的void f(

Android开发之IPC进程间通信-AIDL介绍及实例解析

一.IPC进程间通信 IPC是进程间通信方法的统称,Linux IPC包括以下方法,Android的进程间通信主要采用是哪些方法呢? 1. 管道(Pipe)及有名管道(named pipe):管道可用于具有亲缘关系进程间的通信,有名管道克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允许无亲缘关系进程间的通信:   2. 信号(Signal):信号是比较复杂的通信方式,用于通知接受进程有某种事件发生,除了用于进程间通信外,进程还可以发送信号给进程本身:linux除了支持Unix早期

C++11学习笔记:std::move和std::forward源码分析

std::move和std::forward是C++0x中新增的标准库函数,分别用于实现移动语义和完美转发. 下面让我们分析一下这两个函数在gcc4.6中的具体实现. 预备知识 引用折叠规则: X& + & => X& X&& + & => X& X& + && => X& X&& + && => X&& 函数模板参数推导规则(右值引用参数部分):

移动构造和移动赋值与std::move

---------------------------------------移动构造-------------------------------------------- 传统的深拷贝深赋值 对于类中,含有指针的情况,要自实现其拷贝构造和拷贝赋值.也就是所谓的深拷贝和深赋值.我想这己经成为一种共识了. 比如如下类: #include <iostream> using namespace std; class HasPtrMem { public: HasPtrMem():_d(new in

C++11 std::move 强制转换为右值

[1]std::move 在C++11中,标准库在<utility>中提供了一个有用的函数std::move. 这个函数的名字很具有迷惑性,因为实际上std::move并不能移动任何东西,它唯一的功能:将一个左值强制转化为右值引用,继而可以通过右值引用使用该值,以用于移动语义. 从实现上讲,std::move基本等同于一个类型转换: static_cast<T&&>(lvalue); [2]应用注意项 (1)被std::move转化的左值,其生命期并没有随着转化而改

C++ Primer 笔记——理解std::move

标准库move函数是使用右值引用的模板的一个很好的例子.标准库是这样定义std::move的: template <typename T> typename remove_reference<T>::type&& move(T&& t) { return static_cast<typename remove_reference<T>::type&&>(t); } 我们考虑如下代码的工作过程: std::str