v2 = temp; //
Этот код дважды копирует строку, которая первоначально принадлежала объекту v1: один раз, когда конструктор копий класса HasPtr копирует объект v1 в объект temp, и второй раз, когда оператор присвоения присваивает объект temp объекту v2. Он также копирует строку, которая первоначально принадлежала объекту v2, когда объект v2 присваивается объекту v1. Как уже упоминалось, копирование объекта, подобного значению класса HasPtr, резервирует новую строку и копирует строку, на которую указывает объект класса HasPtr.
В принципе ни одно из этих резервирований памяти не обязательно. Вместо того чтобы резервировать новые копии строки, можно было бы обменять указатели. Таким образом, имело бы смысл обменять два объект класса HasPtr так, чтобы выполнить следующее:
string *temp = v1.ps; //
v1.ps = v2.ps; //
v2.ps = temp; //
//
swap()Переопределить стандартное поведение функции swap() можно, определив в классе ее собственную версию. Вот типичная реализация функции swap():
class HasPtr {
friend void swap(HasPtr&, HasPtr&);
//
};
inline
void swap(HasPtr &lhs, HasPtr &rhs) {
using std::swap;
swap(lhs.ps, rhs.ps); //
swap(lhs.i, rhs.i); //
}
Все начинается с объявления функции swap(), дружественной, чтобы предоставить ей доступ к закрытым переменным-членам класса HasPtr. Поскольку функция swap() предназначена для оптимизации кода, определим ее как встраиваемую (см. раздел 6.5.2). Тело функции swap() вызывает функции swap() каждой из переменных-членов заданного объекта. В данном случае сначала обмениваются указатели, а затем целочисленные члены объектов, связанных с параметрами rhs и lhs.
swap() никогда не бывает обязательной. Однако ее определение может быть важно для оптимизации классов, резервирующих ресурсы.
swap() должны вызвать функции swap(), а не std::swap()В этом коде есть один важный нюанс: хотя в данном случае это не имеет значения, важно, чтобы функция swap() вызвала именно функцию swap(), а не std::swap(). В классе HasPtr переменные-члены имеют встроенные типы. Для встроенных типов нет специализированных версий функции swap(). В данном случае она вызывает библиотечную функцию std::swap().
Но если класс имеет член, тип которого обладает собственной специализированной функцией swap(), то вызов функции std::swap() был бы ошибкой. Предположим, например, что есть другой класс по имени Foo, переменная-член h которого имеет тип HasPtr. Если не написать для класса Foo собственную версию функции swap(), то будет использована ее библиотечная версия. Как уже упоминалось, библиотечная функция swap() осуществляет ненужное копирование строк, управляемых объектами класса HasPtr.
Ненужного копирования можно избежать, написав функцию swap() для класса Foo. Но версию функции swap() для класса Foo можно написать так:
void swap(Foo &lhs, Foo &rhs) {
//
//
std::swap(lhs.h, rhs.h); //
}
Этот код нормально компилируется и выполняется. Однако никакого различия в производительности между этим кодом и просто использующим стандартную версию функции swap() не будет. Проблема в том, что здесь явно запрошен вызов библиотечной версии функции swap(). Однако нужна версия функции не из пространства имен std, а определенная в классе HasPtr.
Правильный способ написания функции swap() приведен ниже.
void swap(Foo &lhs, Foo &rhs) {
using std::swap;
swap(lhs.h, rhs.h); //
//
}