C++Primer 交换操作

除了定义拷贝控制成员,管理资源的类通常还定义了一个名为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.ps
v2.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拷贝到rhs
HasPtr& 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 <iostream>
#include <string>
#include <vector>
#include <algorithm>
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<HasPtr> 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版本不会有更多受益。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值