class HasPtr {
public:
HasPtr(const std::string &s = std::string())
:ps(new std::string(s)), i(0) {}
HasPtr(const HasPtr& p) //拷贝构造函数
:ps(new std::string(*(p.ps))), i(p.i) {}
HasPtr& operator=(const HasPtr& rhs); //拷贝辅助运算符
~HasPtr() { delete ps; }
private:
std::string *ps;
int i;
};
//我们这个operator=可以处理自我赋值的情况
HasPtr& HasPtr::operator=(const HasPtr& rhs)
{
auto newp = new std::string(*(rhs.ps));
delete ps; //释放旧内存
ps = newp; //使用新内存
i = rhs.i;
return *this; //返回自身
}
一、为什么要设计swap操作
- 对于上面的HasPtr类,如果我们希望把两个类进行交换操作,那么一般我们会执行如下的代码:
HasPtr v1, v2;
HasPtr temp = v1; //创建一个临时变量保存v1
v1 = v2; //将v2赋值给v1
v2 = temp; //将原v1赋值给v2
- 在上面的代码代码会执行一次拷贝和两次赋值:
- 拷贝:将v1拷贝给temp
- 将v2赋值给v1,将temo赋值给v2
- 对于上面的拷贝与赋值操作,都会在内部执行string对象的创建。这些内存的分配是不必要的,我们只是希望交换swap的指针,而不用分配string的新副本。因此,理论上对于上面v1和v2的交换操作,我们只交换ps指针就可以了:
//下面是伪代码,因为ps是private的
HasPtr v1, v2;
string *temp = v1.ps;
v1.ps = v2.ps;
v2.ps = temp;
二、编写自己的swap函数
- 现在我们编写一个自定义版本的swap函数,实现如下:
class HasPtr {
public:
//其他代码省略(同上)
friend void swap(HasPtr&, HasPtr&);
};
inline void swap(HasPtr &lhs, HasPtr &rhs)
{
using std::swap;
swap(lhs.ps, lhs.ps);//交换指针,而不是string数据
swap(lhs.i, lhs.i); //交换int成员
}
- 函数格式讲解:
- friend:因为ps和i都是私有成员,因此需要将其定义为friend函数
- inline:为了优化代码
- 与拷贝控制成员不同,swap不是必须要的。但是,对于分配了资源的类,定义swap可能是一种很重要的手段
三、调用自定义的swap,而不是std::swap
- 标准模板库std也定义了一个swpa函数,因此调用自定义的swap函数的时候需要防止与std::swap冲突
- 现在假设有一个Foo类,其中有一个HasPtr类
class HasPtr {};
class Foo
{
public:
HasPtr ptr;
};
void swap(Foo &lhs, Foo &rhs)
{
std::swap(lhs.ptr, rhs.ptr); //此处调用的是std::swap,没有调用我们自定义的swpa函数
}
- 因此我们需要修改代码,使其调用我们上面自定义的swap函数
void swap(Foo &lhs, Foo &rhs)
{
using std::swap;
swap(lhs.ptr, rhs.ptr); //使用HasPtr版本的swap函数
}
四、在赋值运算符中使用swap
- 定义swap的类通常用swap来定义它们的赋值运算符。这些运算符使用了一种名为拷贝并交换的技术
- 代码如下:
class HasPtr {
public:
//其他代码同上
HasPtr& operator=(HasPtr rhs);
};
HasPtr& HasPtr::operator=(HasPtr rhs)
{
//交换左侧对象和局部变量rhs的内容
swap(*this, rhs); //rhs现在只想本对象增使用的内存
return *this;
}//函数结束后,rhs析构,释放rhs中的指针(本对象原来使用的)