51:P418已经说的很清楚,使用的是移动操作,因为返回值相当于一个表达式,为右值
52:rhs是一个非引用的参数,所以需要进行拷贝初始化,依赖于实参的类型,拷贝初始化要么使用拷贝构造函数要么使用移动构造函数,左值被拷贝,右值被移动
hp的第一个赋值中,右侧为左值,需要进行拷贝初始化,分配一个新的string,并拷贝hp2所指向的string
hp的第二个赋值中,直接调用std::move()将一个右值绑定到hp2上,虽然移动构造函数和拷贝构造函数皆可行,但是移动构造函数是精确匹配且不会分配任何内存
53:需要支持C++11新标准
#include<iostream> #include<string> #include<vector> #include<algorithm> using namespace std; class Hasptr1 { friend void swap(Hasptr1&,Hasptr1&); friend bool operator<(const Hasptr1& s1,const Hasptr1& s2); friend void show(vector<Hasptr1>& vec); public: //构造函数,初始化相关成员 Hasptr1(const string& s = string()):ps(new string(s)),i(0),use(new size_t(1)){} //拷贝构造函数,将引用计数也拷贝过来,并且递增引用计数 Hasptr1(const Hasptr1& p):ps(p.ps),i(p.i),use(p.use){++*use;} //拷贝赋值运算符 Hasptr1& operator= (const Hasptr1& p1) { ++*p1.use;//首先递增右侧运算符对象的引用计数 if (--*use == 0)//递减本对象的引用计数,若没有其他用户,则释放本对象的成员 { delete ps; delete use; } ps = p1.ps;//进行拷贝 use = p1.use; i = p1.i; return *this; } Hasptr1(Hasptr1&& p) noexcept : ps(p.ps), i(p.i) { p.ps = 0; std::cout << "call move constructor" << std::endl; } Hasptr1& operator=(Hasptr rhs) { swap(*this, rhs); return *this; } // Hasptr1& operator=(const Hasptr1 &rhs) //{ // auto newp = new std::string(*rhs.ps); // delete ps; // ps = newp; // i = rhs.i; // std::cout << "call copy assignment" << std::endl; // return *this; //} // Hasptr& operator=(Hasptr1 &&rhs) noexcept //{ // if (this != &rhs) // { // delete ps; // ps = rhs.ps; // i = rhs.i; // rhs.ps = nullptr; // std::cout << "call move assignment" << std::endl; // } // return *this; //} //析构函数 ~Hasptr1() { if (*use == 0)//引用计数变为0,说明已经没有对象再需要这块内存,进行释放内存操作 { delete ps; delete use; } } private: //定义为指针,是我们想将该string对象保存在动态内存中 string *ps; size_t *use;//将计数器的引用保存 int i; }; inline void swap(Hasptr1& a,Hasptr1& b) { using std::swap; swap(a.ps,b.ps); std::swap(a.i,b.i); cout<<"123"; } bool operator< (const Hasptr1& s1,const Hasptr1& s2) { cout<<"定义的 Operator< 被调用"<<endl; return *s1.ps < *s2.ps; } void show(vector<Hasptr1>& vec) { vector<Hasptr1>::iterator it1 = vec.begin(); for (it1; it1 != vec.end(); ++it1) { cout<<*(it1->ps)<<endl; } } int main(int argc, char**argv) { // vector<Hasptr1> vec1; // Hasptr1 a("l"); // Hasptr1 b("llll"); // Hasptr1 c("lll"); // vec1.push_back(a); // vec1.push_back(b); // vec1.push_back(c); // vector<Hasptr1>::iterator it1 = vec1.begin(); // sort(vec1.begin(),vec1.end()); // show(vec1); Hasptr1 hp1("hello"), hp2("World"), *pH = new Hasptr1("World"); hp1 = hp2; hp1 = std::move(*pH); return 0; }54:见53代码
55:知识点1:区分拷贝和移动的重载函数通常有一个版本接收const T &(左值引用),一个版本接受T&&(右值引用参数)——引用限定符,这样我们调用函数时,实参的类型决定了新元素是进行拷贝还是移动操作
知识点2:在函数的参数列表之后加上&或者&&表示该函数只能用于左值或者右值
知识点3:当我们定义两个或两个以上的具有同名且参数列表相同的成员函数,必须对所有函数都加上引用限定符或者都不加
void push_back(string &&s) { data->push_back(move(s)); }56:sorted不断调用本身,无限循环,造成堆栈溢出
57:见58
58:
#include <vector> #include <iostream> #include <algorithm> using std::vector; using std::sort; class Foo { public: Foo sorted()&&; Foo sorted() const&; private: vector<int> data; }; Foo Foo::sorted() && { sort(data.begin(), data.end()); std::cout << "&&" << std::endl; // debug return *this; } Foo Foo::sorted() const & { // Foo ret(*this); // sort(ret.data.begin(), ret.data.end()); // return ret; std::cout << "const &" << std::endl; // debug // Foo ret(*this); // ret.sorted(); //13.56 // return ret; return Foo(*this).sorted(); //13.57 } int main() { Foo().sorted(); // call "&&" Foo f; f.sorted(); // call "const &" }
