考虑实现一个不抛异常的swap
Effective C++:参考自harttle land
类的swap实现与STL容器是一致的:提供swap成员函数, 并特化std::swap来调用那个成员函数。
class Widget {
public:
void swap(Widget& other){
using std::swap; // 使得`std::swap`在该作用域内可见
swap(pImpl, other.pImpl);
}
};
namespace std {
template<>
void swap<Widget>(Widget& a, Widget& b){
a.swap(b); // 调用成员函数
}
}
类模板的Swap
1.下面代码不能通过编译。C++允许偏特化类模板,却不允许偏特化函数模板
template<typename T>
class WidgetImpl { ... }; template<typename T>
class Widget { ... }; namespace std {
template<typename T>
// swap后的尖括号表示这是一个特化,而非重载。
// swap<>中的类型列表为template<>中的类型列表的一个特例。
void swap<Widget<T> >(Widget<T>& a, Widget<T>& b){
a.swap(b);
}
}
2. 不能向STL里面添加新的东西,重载版本。C++标准中,客户只能特化std中的模板,但不允许在std命名空间中添加任何新的模板,会引发未定义
namespace std {
template<typename T>
// 注意swap后面没有尖括号,这是一个新的模板函数。
// 由于当前命名空间已经有同名函数了,所以算函数重载。
void swap(Widget<T>& a, Widget<T>& b){
a.swap(b);
}
}
正确做法是不在std下添加swap函数了,把swap定义在Widget所在的命名空间中,或直接在global命名空间:
namespace WidgetStuff {
template<typename T>
class Widget {
public:
void swap(Widget& other){
using std::swap;
swap(pImpl, other.pImpl);
}
};
template<typename T>
void swap(Widget<T>& a, Widget<T>& b){
a.swap(b);
}
}
似乎类的Swap也只需要在同一命名空间下定义swap函数,而不必特化std::swap。 但是!有人喜欢直接写std::swap(w1, w2),调用std::swap,特化WidgetStuff::swap不会得到调用。std::swap可以让你的类更加健壮。此时,C++编译器会优先调用指定了T的std::swap特化版,其次是T所在命名空间下的对应swap函数, 最后才会匹配std::swap的默认实现。
应用:
赋值构造函数的swap写法:解决自己赋值自己的问题
class Widget
{
public:
Widget():id(), size(), data(NULL){}
Widget(int val)
{
id = val;
size = id+;
data = new int [size];
}
Widget(const Widget &rhs) //deep copy
{
id = rhs.id;
size = rhs.size;
data = new int[size];
memcpy(data, rhs.data, sizeof(int)*size);
}
friend void swap(Widget & lhs, Widget &rhs)
{
using std::swap;
swap(lhs.id, rhs.id);
swap(lhs.size, rhs.size);
swap(lhs.data, rhs.data);
}
/*
Widget & operator = (Widget rhs) //pass by value, copy构造后swap
{
swap(*this, rhs);
return *this;
}
*/
Widget& operator= (const Widget &rhs) //pass by 引用
{
Widget tmp(rhs);
swap(*this, tmp);
return *this;
}
~Widget()
{
std::cout<<"id:"<< id << " destructor called..." << std::endl;
if(data != NULL)
{
delete [] data;
data = NULL;
}
}
int getId() const
{
return id;
}
int getSize() const
{
return size;
} private:
int *data;
int id;
int size;
};
如何实现Swap呢?总结一下:
- 提供一个更加高效的,不抛异常的共有成员函数(比如
Widget::swap)。 - 在你类(或类模板)的同一命名空间下提供非成员函数
swap,调用你的成员函数。 - 如果你写的是类而不是类模板,请偏特化
std::swap,同样应当调用你的成员函数。 - 调用时,请首先用
using使std::swap可见,然后直接调用swap。
考虑实现一个不抛异常的swap的更多相关文章
- EC读书笔记系列之13:条款25 考虑写出一个不抛异常的swap函数
记住: ★当std::swap对你的类型效率不高时,提供一个swap成员函数,并确定其不抛出异常 ★若你提供一个member swap,也该提供一个non-member swap来调用前者.对于cla ...
- Effective C++:条款25:考虑写出一个不抛异常的swap函数
(一) 缺省情况下swap动作可由标准程序库提供的swap算法完毕: namespace std { template<typename T> void swap(T& a, T& ...
- 【25】考虑写出一个不抛异常的swap函数
1.swap交换对象值,std提供了实现方法模版的实现,它的实现是交换对象值. namespace std { template<typename T> void swap(T& ...
- [Effective C++ --025]考虑写出一个不抛异常的swap函数
引言 在我的上一篇博客中,讲述了swap函数. 原本swap只是STL的一部分,而后成为异常安全性编程的脊柱,以及用来处理自我赋值可能性. 一.swap函数 标准库的swap函数如下: namespa ...
- Effective C++ -----条款25:考虑写出一个不抛异常的swap函数
当std::swap对你的类型效率不高时,提供一个swap成员函数,并确定这个函数不抛出异常. 如果你提供一个member swap,也该提供一个non-member swap用来调用前者.对于cla ...
- 《Effective C++》item25:考虑写出一个不抛异常的swap函数
std::swap()是个很有用的函数,它可以用来交换两个变量的值,包括用户自定义的类型,只要类型支持copying操作,尤其是在STL中使用的很多,例如: int main(int argc, _T ...
- Effective C++ Item 25 考虑写出一个不抛异常的swap函数
本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie 经验:当std::swap对你的类型效率不高时,提供一个swap成员函数,并确定这个函数不抛 ...
- 【JAVA】学习路径64-补充-编写一个会抛异常的方法
有一些方法,在调用的时候有可能会出错,所以我们使用这些方法的时候会使用try catch. 比如InputStream里面的read()方法等等,那么这些方法是怎么实现抛异常的效果的呢? 能抛异常的方 ...
- 不抛异常的swap函数
namespace AStuff{ template<typename T> class A { public: void swap(A *other) { using std::swap ...
随机推荐
- python3+selenium入门12-警告框处理
在WebDriver中要处理JS生成的alert.confirm以及prompt,需要使用到switch_to_alert()定位到alert/confirm/prompt,然后再使用text.acc ...
- unity开发笔记
debug代码 Debug.Log("普通信息"); Debug.LogWarning("警告信息"); Debug ...
- git与eclipse集成之更新特性分支代码到个人特性分支
1.1. 更新特性分支代码到个人特性分支 在基于特性分支开发的过程中,存在多人向特性分支提交代码的情况,开发者需要关注特性分支代码与个人分支代码保持同步,否则可能导致提交代码冲突. 具体代码同步步骤: ...
- Mudo C++网络库第十章学习笔记
C++编译链接精要 C++语言的三大约束: 与C兼容, 零开销(zero overhead)原则, 值语义; 兼容C语言的编译模型与运行模型, 也就是锁能直接使用C语言的头文件和库; 头文件包含具有传 ...
- button 去掉原生边框
button按钮触发 hover 时,自带边框会显示,尤其是 button 设置圆角时,如图: 解决办法: outline: 0;
- PHP导入导出csv文件 Summer-CSV
2017年11月9日09:25:56 根据项目实践总结的一个类文件, mac/win下没乱码 简体中文 默认从gb2312转到utf-8 https://gitee.com/myDcool/PHP-C ...
- Git如何克隆远程仓库
1.首先选择一个合适的地方创建一个空目录 mkdir learngit 2.通过git Init命令把这个目录变成git可以管理的仓库,瞬间git就把仓库建好了 3.将编写的文件放到 lear ...
- 新手-ios
最近突然让我学习一下ios,之前从未接触过(一脸蒙逼).而且我用的电脑也不是ios操作系统.上网查了下 网友说虚拟机也可以,于是本人从此举用上了ios系统. 需要的安装的工具有: 资源共享给大家: h ...
- tcp协议简单了解
HTTP简介 HTTP协议是Hyper Text Transfer Protocol(超文本传输协议)的缩写,是用于从万维网(WWW:World Wide Web )服务器传输超文本到本地浏览器的传送 ...
- vue v-time指令封装(接口返回时间戳 在到日期转换)
// 全局时间戳转换指令注册Vue.directive('time',{ bind: function (el,binding) { let getTime = new Date(binding.va ...