标准库中swap的缺点
如果一个类定义了自己的swap,那么算法将使用类自定义版本,否则算法将使用标准库定义的swap,标准库定义的swap在交换两个对象时需要进行一次拷贝和两次赋值,理论上这些内存分配都是不必要的,我们更希望swap交换指针,而不是分配对象的新副本。
template <class _Ty, class>
inline void swap(_Ty& _Left, _Ty& _Right) _NOEXCEPT_COND(is_nothrow_move_constructible_v<_Ty>&&
is_nothrow_move_assignable_v<_Ty>) { // exchange values stored at _Left and _Right
_Ty _Tmp = _STD move(_Left);
_Left = _STD move(_Right);
_Right = _STD move(_Tmp);
}
class Person
{
public:
const char* name_;
Person(const char* name) :name_(name) {
std::cout << "call Person::Person(name)" << std::endl;
};
Person(const Person& person) {//拷贝构造函数
std::cout << "call Person::Person(person)" << std::endl;
name_ = person.name_;
};
Person& operator=(const Person& person) {//拷贝赋值运算符
std::cout << "call Person::operator=" << std::endl;
name_ = person.name_;
return *this;
}
};
int main()
{
Person person1{ "xiao hong" };
Person person2{ "xiao ming" };
swap(person1,person2);
system("pause");
}
一次拷贝和两次赋值:
call Person::Person(name)
call Person::Person(name)
call Person::Person(person)
call Person::operator=
call Person::operator=
编写我们自己的swap函数
当有多个重载模板对一个调用提供同样好的匹配时,应选择最特例化的版本,所以下面的代码中我们自定义的swap匹配程度会优于std中定义的版本。
void swap(Person& person1, Person& person2)
{
cout<<"swap person"<<endl;
swap(person1.name_, person2.name_);
}
int main()
{
Person person1{ "xiao hong" };
Person person2{ "xiao ming" };
swap(person1,person2);
cout << person1.name_ << endl;//xiao ming
cout << person2.name_ << endl;//xiao hong
system("pause");
}
在赋值运算符中使用swap
定义swap的类通常用swap来定义它们的赋值运算符,这些运算符使用了一种名为拷贝并交换的技术,这种技术将左侧运算对象与右侧运算对象的一个副本进行交换。
在这个版本的赋值运算符中,参数并不是引用,而是右侧运算对象的一个副本,然后我们调用swap交换参数和*this中的数据成员,在赋值运算符结束时,参数被销毁,释放掉了左侧运算对象中原来的内存(如果申请了的话)。
void swap(Person& person1, Person& person2)
{
cout<<"swap person"<<endl;
swap(person1.name_, person2.name_);
}
Person& Person::operator=(Person person) {
swap(*this, person);
return *this;
}
int main()
{
Person person1{ "xiao hong" };
Person person2{ "xiao ming" };
person1 = person2;
cout << person1.name_ << endl;//xiao ming
cout << person2.name_ << endl;//xiao ming
system("pause");
}