本文共 3240 字,大约阅读时间需要 10 分钟。
除了定义拷贝控制成员,管理资源的类通常还定义了一个名为swap的函数。为了交换两个对象我们需要进行一次拷贝和两次赋值。
HasPtr temp = v1; //创建v1的值的一个临时副本v1 = v2; //将v2的值赋予v1()v2 = temp; //将保存的v1的值赋予v2
拷贝一个类值的HasPtr会分配一个新string并将其拷贝到HasPtr指向的位置,而这些拷贝中的内存分配都是不必要的。我们更希望swap交换指针,而不是分配string的新副本
string *temp = v1.ps; //为v1.ps中的指针创建一个副本v1.ps = v2.ps; //将v2.ps中的指针赋予v1.psv2.ps = temp; //将保存的v1.ps中原来的指针赋予v2.ps
编写自己的swap函数
class HasPtr{ friend void swap(HasPtr&, HasPtr&);};inline swap(HasPtr &lhs, HasPtr &rhs){ using std::swap; swap(lhs.ps, rhs.ps); swap(lhs.i, rhs.i);}
如果一个类成员有自己的swap函数版本,就不能调用标准库的std::swap(),应该直接调用类型特定的swap函数
在赋值运算中使用swap 定义了swap函数的类,通常用swap来定义它们的赋值运算符,这些运算符使用了一种名为拷贝并交换(copy and swap)的技术。这种技术将左侧运算对象与右侧对象的一个副本进行交换://注意rhs是按值传递的,意味着HasPtr的拷贝构造函数将右侧运算对象中的string拷贝到rhsHasPtr& HasPtr::operator=(HasPtr rhs){ //交换左侧运算对象和局部变量rhs的内容 swap(*this, rhs); return *this;}
该版本中,并非是引用传递,而是值传递,rhs是右侧运算对象的一个副本,参数传递时拷贝HasPtr的操作会分配该对象string的一个新副本。
在swap调用之后,*this中的指针成员指向新分配的string——右侧运算对象中string的一个副本。当赋值运算符结束时,rhs被销毁,HasPtr中的析构函数被执行。此析构函数delete rhs现在指向的内存,即,释放掉左侧运算对象中原来的内存。 这个技术自动处理了自赋值的情况,它通过在改变左侧运算对象之前拷贝右侧对象保证了字赋值的正确。解释:swap(HasPtr&, HasPtr&)中对swap的调用不会导致递归循环
解答:在swap函数中又调用了swap来交换ps和i,但这两个类型为指针和整型,属于内置类型。因此函数调用中的swap调用被解析为std::swap,而不是HasPtr特定版本的swap函数,所以不会导致递归调用。为你的类值版本的HasPtr类编写swap函数
inline void swap(HasPtr &lhs, HasPtr &rhs){ cout << " 交换 " << *lhs.ps << " 和 " << *rhs.ps << endl; swap(lhs.ps, rhs.ps); //交换指针 swap(lhs.i, rhs.i); //交换int成员 }
为你的HasPtr类定义一个<运算符,并定义一个HasPtr的vector。为这个vector添加一些元素,并对它执行sort。注意何时会调用swap
<运算符直接返回两个HasPtr的ps指向的string的比较结果即可,但需要注意的是,它应该被声明为const。#include#include #include #include using namespace std;class HasPtr { friend void swap(HasPtr&, HasPtr&);public: HasPtr(const string &s = string()):ps(new string(s)),i(0){ } //默认构造函数 HasPtr(const HasPtr& p):ps(new string(*p.ps)), i(p.i) { } //拷贝构造函数 HasPtr& operator=(const HasPtr&); //拷贝赋值运算符 HasPtr& operator=(const string&); //赋予新string string& operator*(); //解引用 bool operator<(const HasPtr&) const; //比较运算 ~HasPtr(); //析构函数private: string* ps; int i;};HasPtr::~HasPtr() { delete ps; //释放string的内存}inline HasPtr& HasPtr::operator=(const HasPtr & rhs) { auto newps = new string(*rhs.ps); //拷贝指针指向的对象 delete ps; //销毁原string ps = newps; //指向新的string i = rhs.i; //使用内置的int赋值 return *this; //返回此对象的引用}HasPtr& HasPtr::operator=(const string& rhs) { *ps = rhs; return *this;}string& HasPtr::operator*() { return *ps;}inline void swap(HasPtr& lhs, HasPtr& rhs) { using std::swap; cout << " 交换 " << *lhs.ps << " 和 " << *rhs.ps << endl; swap(lhs.ps, rhs.ps); //交换指针 swap(lhs.i, rhs.i); //交换int成员 }bool HasPtr::operator<(const HasPtr& rhs) const { return *ps < *rhs.ps;}int main() { vector v; int n = atoi(__argv[1]); for (int i = 0; i < n; i++) v.push_back(to_string(n - i)); for (auto p : v) { cout << *p << " "; } cout << endl; sort(v.begin(), v.end()); for (auto p : v) { cout << *p << " "; } cout << endl; return 0;}
类指针的HasPtr版本会从swap函数中受益吗?
默认的swap版本简单交换两个对象的非静态成员,对于HasPtr来言,就是交换string指针ps、引用计数指针use和整型值i。因此,默认的swap版本能够正确处理类指针HasPtr的交换,专用swap版本不会有更多受益。转载地址:http://wtxmb.baihongyu.com/