Last active
October 22, 2019 03:03
-
-
Save lixingcong/11493db47651b7f04da4e660d2abc731 to your computer and use it in GitHub Desktop.
C++11的右值引用:移动语义+完美转发
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include <iostream> | |
| #include <memory> | |
| #include <utility> | |
| // 源码已稍作修改,出自:https://en.cppreference.com/w/cpp/utility/forward | |
| struct A | |
| { | |
| A(int&& n) { std::cout << "rvalue overload, n=" << n << "\n"; } | |
| A(int& n) { std::cout << "lvalue overload, n=" << n << "\n"; } | |
| }; | |
| class B | |
| { | |
| public: | |
| template<class T1, class T2, class T3> | |
| B(T1&& t1, T2&& t2, T3&& t3) | |
| : a1_{std::forward<T1>(t1)} | |
| , a2_{std::forward<T2>(t2)} | |
| , a3_{std::forward<T3>(t3)} | |
| {} | |
| private: | |
| const A a1_, a2_, a3_; | |
| }; | |
| // unique_ptr是独占型的智能指针,它不允许其他的智能指针共享其内部的指针,不允许通过赋值将一个unique_ptr赋值给另一个unique_ptr | |
| template<class T, class U> | |
| std::unique_ptr<T> make_unique1(U&& u) | |
| { | |
| return std::unique_ptr<T>(new T(std::forward<U>(u))); | |
| } | |
| template<class T, class... U> | |
| std::unique_ptr<T> make_unique2(U&&... u) | |
| { | |
| return std::unique_ptr<T>(new T(std::forward<U>(u)...)); | |
| } | |
| int main() | |
| { | |
| std::cout << "A" << std::endl; | |
| auto p1 = make_unique1<A>(2); // rvalue | |
| int i = 1; | |
| auto p2 = make_unique1<A>(i); // lvalue | |
| std::cout << "B" << std::endl; | |
| auto t = make_unique2<B>(2, i, 3); | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include <iostream> | |
| #include <cstring> | |
| #include <vector> | |
| // C++11的右值引用 | |
| // https://www.ibm.com/developerworks/cn/aix/library/1307_lisl_c11/index.html | |
| class StringBase | |
| { | |
| protected: | |
| char* _data; | |
| size_t _len; | |
| void _init_data(const char* s) | |
| { | |
| _data = new char[_len + 1]; | |
| memcpy(_data, s, _len); | |
| _data[_len] = '\0'; | |
| } | |
| public: | |
| StringBase() | |
| { | |
| _data = nullptr; | |
| _len = 0; | |
| } | |
| virtual ~StringBase() | |
| { | |
| if (_data) | |
| delete[] _data; | |
| } | |
| StringBase(const char* p) | |
| { | |
| _len = strlen(p); | |
| _init_data(p); | |
| } | |
| StringBase(const StringBase& str) | |
| { | |
| // 传统的拷贝构造函数 | |
| _len = str._len; | |
| _init_data(str._data); | |
| std::cout << "Copy Constructor is called! source: " << str._data << std::endl; | |
| } | |
| StringBase& operator=(const StringBase& str) | |
| { | |
| // 传统的拷贝赋值操作符 | |
| if (this != &str) { | |
| if (_data) | |
| delete[] _data; | |
| _len = str._len; | |
| _init_data(str._data); | |
| } | |
| std::cout << "Copy Assignment is called! source: " << str._data << std::endl; | |
| return *this; | |
| } | |
| }; | |
| class StringMoveAssign : public StringBase | |
| { | |
| public: | |
| StringMoveAssign() | |
| : StringBase() | |
| {} | |
| StringMoveAssign(const char* p) | |
| : StringBase(p) | |
| {} | |
| StringMoveAssign(const StringMoveAssign& str) // 传统的拷贝构造函数 | |
| : StringBase(str) | |
| {} | |
| StringMoveAssign& operator=(const StringMoveAssign& str) // 传统的拷贝赋值操作符 | |
| { | |
| if (this != &str) { | |
| if (_data) | |
| delete[] _data; | |
| _len = str._len; | |
| _init_data(str._data); | |
| } | |
| std::cout << "Copy Assignment is called! source: " << str._data << std::endl; | |
| return *this; | |
| } | |
| StringMoveAssign(StringMoveAssign&& str) | |
| { | |
| /* 转移构造函数和拷贝构造函数类似,有几点需要注意: | |
| * 1. 参数(右值)的符号必须是右值引用符号,即“&&”。 | |
| * 2. 参数(右值)不可以是常量,因为我们需要修改右值。 | |
| * 3. 参数(右值)的资源链接和标记必须修改。否则,右值的析构函数就会释放资源。转移到新对象的资源也就无效了。 | |
| */ | |
| std::cout << "Move Constructor is called! source: " << str._data << std::endl; | |
| _len = str._len; | |
| _data = str._data; | |
| str._len = 0; | |
| str._data = nullptr; | |
| } | |
| StringMoveAssign& operator=(StringMoveAssign&& str) | |
| { | |
| // 转移赋值操作符 注意要点:与上面的拷贝构造函数一样 | |
| std::cout << "Move Assignment is called! source: " << str._data << std::endl; | |
| if (this != &str) { | |
| if (_data) | |
| delete[] _data; | |
| _len = str._len; | |
| _data = str._data; | |
| str._len = 0; | |
| str._data = nullptr; | |
| } | |
| return *this; | |
| } | |
| }; | |
| template<class T> | |
| void slowSwap(T& a, T& b) | |
| { | |
| T tmp(a); // copy a to tmp | |
| a = b; // copy b to a | |
| b = tmp; // copy tmp to b | |
| } | |
| template<class T> | |
| void fastSwap(T& a, T& b) | |
| { | |
| T tmp(std::move(a)); // move a to tmp | |
| a = std::move(b); // move b to a | |
| b = std::move(tmp); // move tmp to b | |
| } | |
| int main() | |
| { | |
| { | |
| std::cout << "------ move assign test -------" << std::endl; | |
| StringBase s1; | |
| s1 = StringBase("StringBase 1111"); | |
| std::vector<StringBase> vec1; | |
| vec1.push_back(StringBase("StringBase 2222")); | |
| // 对右值调用了转移构造函数和转移赋值操作符。节省了资源,提高了程序运行的效率。 | |
| // 有了右值引用和转移语义,我们在设计和实现类时,对于需要动态申请大量资源的类,应该设计转移构造函数和转移赋值函数 | |
| StringMoveAssign s2; | |
| s2 = StringMoveAssign("StringMoveAssign 1111"); | |
| std::vector<StringMoveAssign> vec2; | |
| vec2.push_back(StringMoveAssign("StringMoveAssign 2222")); | |
| } | |
| { | |
| std::cout << "------ std::move test -------" << std::endl; | |
| { | |
| std::cout << " *slow" << std::endl; | |
| StringMoveAssign s1("s1"); | |
| StringMoveAssign s2("s2"); | |
| slowSwap(s1, s2); | |
| } | |
| { | |
| std::cout << " *fast" << std::endl; | |
| StringMoveAssign s1("s1"); | |
| StringMoveAssign s2("s2"); | |
| fastSwap(s1, s2); | |
| } | |
| } | |
| return 0; | |
| } |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
右值引用的出现是为了实现移动语义(std::move),顺便解决完美转发的问题(std::forward)