swap是两个元素交换的函数,在STL中也有对应的实现,其基本实现为:
namespace std{
template<typename T>
void swap(T& a, T& b){
T temp(a);
a = b;
b = temp;
}
}
这个置换方式和大多数人想象的一样,它涉及到三个对象的复制:
a复制到temp
b复制到a
temp复制到a
假设有种很大的数据类型
class WidgetImpl{
public:
...
private:
int a,b,c;
std::vector<double> d;
...
};
标准方法涉及到了三次复制,数据很多,复制时间很长,效率显然不给力,有没有更给力的方式呢?
有,指针交换
class Widget { // 这个class使用pimpl手法
public:
Widget( const Widget& rhs );
Widget& operator=( const Widget& rhs ) // 复制Widget时
{ //令它复制其WidgetImpl对象
...
*pImpl = *(rhs.pImpl);
...
}
...
private:
WidgetImpl* pImpl; //指针,指向对象内含的widget数据
};
注解:
PIMPL(Private Implementation 或 Pointer to Implementation)是通过一个私有的成员指针,将指针所指向的类的内部实现数据进行隐藏。
1)降低模块的耦合。因为隐藏了类的实现,被隐藏的类相当于原类不可见,对隐藏的类进行修改,不需要重新编译原类。
2)降低编译依赖,提高编译速度。指针的大小为(32位)或8(64位),x.h发生变化,指针大小却不会改变,文件c.h(包含x.h)也不需要重编译。
3)接口与实现分离,提高接口的稳定性。
但是显然不能直接用标准swap交换PIMPL后的类Widget ,这样它会复制三个Widget +WidgetImpl,会更低效。我们只需要置换Widget中的指针而已。
那我们在Widget中写一个置换的函数:
class Widget { // 这个class使用pimpl手法
public:
Widget( const Widget& rhs );
Widget& operator=( const Widget& rhs ) // 复制Widget时
{ //令它复制其WidgetImpl对象
...
*pImpl = *(rhs.pImpl);
...
}
void swap(Widget& other)
{
using std::swap;
swap(pImpl, other.pImpl);
}
...
private:
WidgetImpl* pImpl; //指针,指向对象内含的widget数据
};
再写 一个std中swap的重载版本:
namespace std {
template<typename T>
void swap(Widget<T>& a,Widget<T>& b)
{ a.swap(b); }
}
注意:我们并不能让这个函数模板篇特化,如以下代码
namespace std {
template<typename T> //std::swap重载版本
void swap<Widget>( Widget& a,Widget& b )
{
a.swap(b);
}
}
这个看起来合情合理,但是不合法。
因为我们企图偏特化一个function template(std::swap),但C++只允许对class templates进行偏特化,在function templates身上偏特化是行不通的。
这段代码不该通过编译(虽然有些编译器错误地接受了它)。
当你打算偏特化一个function templates时,惯常的做法是简单地为它添加一个重载模板。
所以整个代码如下:
class WidgetImpl{
public:
...
private:
int a,b,c;
std::vector<double> d;
...
};
class Widget { // 这个class使用pimpl手法
public:
Widget( const Widget& rhs );
Widget& operator=( const Widget& rhs ) // 复制Widget时
{ //令它复制其WidgetImpl对象
...
*pImpl = *(rhs.pImpl);
...
}
...
private:
WidgetImpl* pImpl; //指针,指向对象内含的widget数据
};
namespace std {
template<typename T>
void swap( Widget& a,Widget& b )
{
a.swap(b);
}
}
最后的测试成功的代码:
#include <iostream>
#include "vector"
class WidgetImpl{
public:
WidgetImpl(int a, int b, int c):a(a),b(b),c(c)
{}
void look(){
std::cout << a << b << c << std::endl;
}
private:
int a,b,c;
std::vector<double> d;
};
class Widget { // 这个class使用pimpl手法
public:
Widget(int a,int b, int c):pImpl(new WidgetImpl(a,b,c))
{}
Widget( const Widget& rhs );
Widget& operator=( const Widget& rhs ) // 复制Widget时
{ //令它复制其WidgetImpl对象
*pImpl = *(rhs.pImpl);
}
void look(){pImpl->look();}
void swap(Widget& other)
{
using std::swap;
swap(pImpl, other.pImpl);
}
private:
WidgetImpl* pImpl; //指针,指向对象内含的widget数据
};
namespace std {
template<> //std::swap重载版本
void swap(Widget& a,Widget& b )
{
a.swap(b);
}
}
int main(){
Widget a(1,2,3),b(4,5,6);
std::swap(a, b);
a.look();
b.look();
}
输出:
456
123