C++ 移动语义--std::move 实例解析
移动语义--std::move
- 编译器只对右值引用才能调用转移构造函数和转移赋值函数,而所有命名对象都只能是左值引用,如果已知一个命名对象不再被使用而想对它调用转移构造函数和转移赋值函数,也就是把一个左值引用当做右值引用来使用,怎么做呢?标准库提供了函数 std::move,这个函数以非常简单的方式将左值引用转换为右值引用。
- 对于右值引用而言,它本身是右值么? 要看情况。
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 }