拷贝赋值运算符: 使用swap操作
2023年12月30日 2024年1月1日
标准库提供了swap操作, 模板函数
我们也可以为类定义swap操作, 作为类的友元
对类类型调用swap操作时, 自定义swap的匹配优先级高于标准库提供的swap
swap不是必要的, 但对于分配了资源的类, 可作为一种很重要的优化手段
比较拷贝赋值和交换操作
-
拷贝赋值: 需要一次拷贝和两次赋值
1Foo a, b; 2Foo temp = a; 3a = b; 4b = temp;
-
交换操作: 可以减少内存分配
1Foo a, b; 2swap(a, b);
为HasPtr的类值版本实现swap操作
1class HasPtr 2{ 3 friend void swap(HasPtr &, HasPtr &); 4public: 5 HasPtr(const string &s = string()) : ps(new string(s)), i(0) { } 6 HasPtr(const HasPtr &p) : ps(new string(*p.ps), i(p.i)) { } 7 HasPtr &operator=(const HasPtr &); 8 ~HasPtr() { delete ps; } 9 10private: 11 string *ps; 12 int i; 13}; 14 15HasPtr &HasPtr::operator=(const HasPtr &rhs) 16{ 17 auto newp = new string(*rhs.ps); 18 delete ps; 19 ps = newp; 20 i = rhs.i; 21 22 return *this; 23} 24 25inline void swap(HasPtr &lhs, HasPtr &rhs) 26{ 27 swap(lhs.ps, rhs.ps); 28 swap(lhs.i, rhs.i); 29}
在赋值运算符中使用swap
第3种赋值运算符重载
定义了swap的类通常用swap来定义他们的赋值运算符, 可以将这种重载方式描述为拷贝并交换 copy and swap
- 使用右侧运算对象拷贝初始化rhs, 其拥有动态内存, 且一定不同于
*this
- 完成交换后, this拥有rhs的动态内存空间, 是新分配的
- 函数结束后, 析构函数销毁rhs, 释放this的原动态内存
自动处理了自赋值情况, 且天然异常安全
1HasPtr &HasPtr::operator=(HasPtr rhs) 2{ 3 swap(*this, rhs); 4 return *this; 5}
异常安全: 发生异常时不影响原对象