考虑写出一个不抛异常的swap 函数

news2024/11/24 5:19:07

 目录

一.标准库中的swap函数

二.针对于非模板类,设计全特化的std::swap()

三.针对模板类

四.合理使用using

五.swap成员函数不能抛出异常

六.swap函数总结


一.标准库中的swap函数

在C++11中有move函数,它可以是一个左值变为右值,在许多场景下可以提高效率。可以参考这篇博客:

C++中move的使用_真爱是蓝色的博客-CSDN博客_c++ move

C++11介绍_"派派"的博客-CSDN博客

补充:

C++11 新增了两个:移动构造函数和移动赋值运算符重载。
针对移动构造函数和移动赋值运算符重载有一些需要注意的点如下:
1.如果你没有自己实现移动构造函数,且没有实现析构函数 、拷贝构造、拷贝赋值重载中的任意一个 。那么编译器会自动生成一个默认移动构造。 默认生成的移动构造函数,对于内置类型成员会执行逐成员按字节拷贝,自定义类型成员,则需要看这个成员是否实现移动构造,如果实现了就调用移动构造,没有实现就调用拷贝构造。
2.如果你没有自己实现移动赋值重载函数,且没有实现析构函数 、拷贝构造、拷贝赋值重载中的任意一个,那么编译器会自动生成一个默认移动赋值。默认生成的移动构造函数,对于内置类型成员会执行逐成员按字节拷贝,自定义类型成员,则需要看这个成员是否实现移动赋值,如果实现了就调用移动赋值,没有实现就调用拷贝赋值。 ( 默认移动赋值跟上面移动构造完全类似 )
 

在一些自定义类型的数据上,如果没有移动构造,移动赋值的话,在某些场景下,那么该如何用swap提高效率呢?

二.针对于非模板类,设计全特化的std::swap()

某些情况下,并不适合用std::swap()。

例如:

例如:

widget w1;
widget w2;
std::swap(w1,w2);

一旦要置换两个widget对象值,我们唯一需要做的就是置换其pImpl指针,但缺省的swap算法不知道这一点。它不只复制三个widgets,还复制三个 widgetImpl对象。

使用全特化的std::swap()函数,例如:

namespace std {
    //template<>用于表示这是一个std::Swap的全特化版本
    template<>
    void swap<Widget>(Widget& a, Widget& b) 
    {
        //错误的,pImpl是private的,无法编译通过
        swap(a.pImpl, b.pImpl);
    }
}

但pImpl是private的,因此函数无法编译通过,解决办法:为类设计一个swap成员函数,并设计一个全局swap函数,或者将上面那个特化swap设置为friend(不推荐),例如:


class Widget
{
public:
    void swap(Widget& rhs)
    {
        using std::swap;        
        swap(pImpl, rhs.pImpl); //调用std::swap()函数,只需交换两个对象的指针
    }
private:
    WidgetImpl* pImpl;
};
 
namespace std {
    template<>
    void swap<Widget>(Widget& a, Widget& b) 
    {
        a.swap(b); //调用Widget::swap()成员函数
    }
}

补充:上面我们是在std中偏特化了std::swap()函数,但是我们不建议这样做,因为这样可能会对std命名空间造成污染,一种解决解决方法是在std命名空间之外定义全特化swap()函数。

三.针对模板类

如果上面的两个类是模板类呢?例如:


template<typename T>
class WidgetImpl { };
 
template<typename T>
class Widget { };

C++只允许对类模板偏特化,不允许对函数模板偏特化,所以下面的代码是无法编译通过的,例如:


 namespace std {
    //此处是错误的,C++只允许对类模板偏特化,不允许对函数模板偏特化
    template<typename T>
    void swap<Widget<T>>(Widget<T>& a, Widget<T>& b)
    {
        a.swap(b);
    }
}

那么该如何解决呢?可以使用重载版本代替偏特化,例如:


template<typename T>
class WidgetImpl{ //同上 };
 
template<typename T>
class Widget{ //同上 };
 
namespace std{
    //这里std::swap的一个重载版本,而不是特化版本,swap后没有<....>
    template<typename T>
    void swap(Widget<T>& a, Widget<T>& b)
    {
        a.swap(b);
    }
}

上面我们重载了std::swap函数,但是是在std命名空间中进行重载的,std是个特殊的命名空间,其管理规则比较特殊,因此我们不建议在std中重载任何内容

一种解决方法就是在自己的命名空间中定义swap()函数。例如:

 补充:

1.我们在自己的命名空间中定义的swap()函数不属于std::swap()的重载版本,因为它们作用域不一致
2.此处我们以命名空间为例,其实不使用命名空间也可以,我们主要是为了指出不要与std::swap()产生冲突。但是为了让全局数据空间太过杂乱,我们建议使用命名空间
3.不要在std中进行全特化。因此处的方法不仅适用于模板类,同样也适用于非模板类

四.合理使用using

假设此时我们编写了一个函数模板其接受两个参数,并在模板内调换两个元素。代码如下:

template<typename T>
void doSomething(T& obj1,T& obj2)
{
    //...
    swap(obj1,obj2);  //调用哪一个版本的swap()函数哪?
    //...
}

应该调用哪个swap?是std既有的那个一般化版本?还是某个可能存在的特化版本?抑或是一个可能存在的T专属版本而且可能栖身于某个命名空间(但当然不可以是std)内?你希望的应该是调用T专属版本,并在该版本不存在的情况下调用std内的一般化版本。

合理做法:

 若果是这样使用呢?

 这便强迫编译器只认std内的swap(包括其任何template特化),因而不再可能调用一个定义于它处的较适当T专属版本。若在std中特化了也行。

五.swap成员函数不能抛出异常

成员本的swap函数绝不可能抛出异常。因为swap的一个最好的应用是帮助类(和类模板)提供强烈的异常安全性保障(条款29介绍)
但此技术基于一个假设:成员版的swap绝不抛出异常。这一约束只施加于swap成员版本,不可施加于非成员版本,因为swap缺省版本是以拷贝构造函数和拷贝赋值运算符为基础,而一般情况下两者都允许抛出异常
因此当你写下一个自定义的swap,往往提供的不只是高效置换对象的版本,而且还不抛出异常

六.swap函数总结


1.当std::swap对你的类型效率不高时,提供一个swap成员函数,并确定这个函数不抛出异     常
2.如果你提供一个member swap,也该提供一个non-member swap用来调用前者。对于       class(而非template class),也请特化std::swap
3.调用swap时应针对std::swap使用using声明式,然后调用swap并且不带任何“命名空间n    资格修饰”
4.用“用户定义类型”进行std template全特化时最好的,但千万不要尝试在std内加入某些对    std而言全新的东西

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/158189.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

mybatis进阶

Mybatis的各种查询功能 MyBatis的各种查询功能&#xff1a; * 1、若查询出的数据只有一条 * a>可以通过实体类对象接收 * b>可以通过list集合接收 * c>可以通过map集合接收 * 结果&#xff1a;{password123456, sex男, id3, age23, email12345qq.com, usernameadmin} …

STM32 按键模块化

文章目录前言一、按键的原理图二、按键的GPIO配置总结前言 本篇文章将继续带大家学习模块化编程&#xff0c;今天主要给大家讲解按键的模块化。 一、按键的原理图 我们可以看到按键分别接到了板子的PE3和PE4引脚。 按键的具体原理这里我就不多讲了&#xff0c;大家可以看我之…

星光starfive2开发板的gpio使用

starfive2开发板是riscv平台不错的板子。 发展非常快。在starfive1的基础上有大量的改进和提升。 板子上的GPIO引脚定义是这样定义的&#xff1a;如下图所示 右侧6&#xff0c;8&#xff0c;10是调试用的串口&#xff0c;跟树莓派是一致的。 GPIO引脚的编号跟树莓派不一样。…

Java多线程:多线程同步安全问题的 “三“ 种处理方式 ||多线程 ”死锁“ 的避免 || 单例模式”懒汉式“的线程同步安全问题

Java多线程&#xff1a;多线程同步安全问题的 “三“ 种处理方式 ||多线程 ”死锁“ 的避免 || 单例模式”懒汉式“的线程同步安全问题 每博一文案 常言道&#xff1a;“不经一事&#xff0c;不懂一人”。 一个人值不值得交往&#xff0c;在关键时候才能看得清。看过这样的一个…

ESP32设备驱动-HMC5883L磁场传感器驱动

HMC5883L磁场传感器驱动 1、HMC5883L介绍 霍尼韦尔 HMC5883L 是一款表面贴装、多芯片模块,专为低场磁场传感而设计,具有数字接口,适用于低成本罗盘和磁力计等应用。 HMC5883L 包括我们先进的高分辨率 HMC118X 系列磁阻传感器和一个 ASIC,该 ASIC 包含放大、自动消磁带驱动…

二叉平衡树之AVL树【手动实现代码】

目录 1、AVL树的概念 2、AVL树定义节点 3、AVL树的插入 4、AVL树的旋转 4.1、新节点插入较高左子树的左侧——右单旋 4.2、新节点插入较高右子树的右侧——左单旋 4.3、新节点插入较高左子树的右侧——左右双旋 4.4、新节点插入较高右子树的左侧——右左双旋 5、AVL树…

【LeetCode】1807. 替换字符串中的括号内容

1807. 替换字符串中的括号内容 题目描述 给你一个字符串 s &#xff0c;它包含一些括号对&#xff0c;每个括号中包含一个 非空 的键。 比方说&#xff0c;字符串 “(name)is(age)yearsold” 中&#xff0c;有 两个 括号对&#xff0c;分别包含键 “name” 和 “age” 。 你…

2023 年 The Sandbox 生态系统将迎来什么?

2022 年对于 The Sandbox 来说是多么美好的一年&#xff01;不仅是对我们的团队来说&#xff0c;对所有与我们建立业务的合作伙伴、才华横溢的创作者、工作室和代理机构来说也是这样。感谢大家让今年最喜欢的时刻成为现实&#xff0c;并成为这个社区的一部分。我们正在共同构建…

25w粉拿下1600w播放,仅用一周时间出圈B站!

2022年&#xff0c;B站举办了第四次跨年晚会《最美的夜》&#xff0c;艾薇儿登台唱起《Complicated》的瞬间&#xff0c;B站跨晚的直播间人气峰值到达3亿。在过去的一年里&#xff0c;平台持续变化带来亮眼成绩&#xff0c;月活用户破3亿、直播开通购物专区、竖屏模式增长播放等…

LaoCat带你认识容器与镜像(四【上】)

Dockerfile是实际项目生产中&#xff0c;比较常用的一个知识点&#xff0c;故也准备分成上下俩节来讲解。 本章内容 如何查询相关Dockerfile与Dockerfile基础命令介绍 本文实操全部基于Ubuntu 20.04 宿主机 > linux服务器本身 业务不断的扩充累积中&#xff0c;大多数企业应…

Python import自定义模块报错、自定义异常、字符串处理、截取

一、python import自定义的模块报错 问题现象&#xff1a;pycharm中运行一切正常&#xff0c;但是到命令行中&#xff08;cmd命令行或pycharm的Terminal窗口&#xff09;运行py文件&#xff0c;就会报错No module named xxx 问题原因&#xff1a; pycharm在每次运行时&#x…

【C进阶】指针的进阶

家人们欢迎来到小姜的世界&#xff0c;<<点此>>传送门 这里有详细的关于C/C/Linux等的解析博客&#xff0c;家人们赶紧冲鸭&#xff01;&#xff01;&#xff01; 客官&#xff0c;码字不易&#xff0c;来个三连支持一下吧&#xff01;&#xff01;&#xff01;关注…

祝大家兔年 新春快乐Happy new year

春节&#xff0c;也被称为农历新年。对于中国人来说&#xff0c;这是规模最大&#xff0c;最重要的传统节日。The Spring Festival is also called Chinese Lunar New Year. Being one of the traditional Chinese festivals, it is the grandest and most important festival …

Ue4 Insights的使用

1.运行UnrealInsights.exe 2.执行独立进程或者打包exe 这时会发现Insights自动创建并开始运行了一个Trace Sessions&#xff0c;持续记录到.utrace文件中 .utrace文件路径 3.也可以通过连接IP地址&#xff0c;获取到该计算机的UE程序。状态为LIVE实时 4.点击右下角Open按钮…

Redis客户端命令基础操作一

查看所有key 语法: keys * 是否存在key 语法: exists [key] 获取包含指定字符串的key 语法&#xff1a; keys *[字符串]* 设置key 语法&#xff1a;set [key] [value] 设置key 语法&#xff1a; setex 【key】【过期时间&#xff08;单位秒&#xff09;】【value】 key重…

时隔 20 年,这个编程语言再次“称王”

近日&#xff0c;全球知名的编程语言流行度排行榜网站 TIOBE 公布了 1 月编程指数信息。前三的编程语言是Python、C 和C&#xff0c;第四为Java&#xff0c;第五是C#。 TIOBE 的 2022 年度编程语言最终花落 C&#xff0c;也是它时隔 20 年后第二次赢得这一称号。 “年度编程语…

行云创新受邀出席2023中国(深圳)阿联酋(迪拜)经贸合作交流会

1月10日&#xff0c;2023中国&#xff08;深圳&#xff09;-阿联酋&#xff08;迪拜&#xff09;经贸合作交流会成功举办。本次交流会充分展示了深圳和迪拜两地城市营商环境和政策优势&#xff0c;并围绕科技创新、数字经济、港口物流等领域发展经验展开分享&#xff0c;来自两…

Linux网络常用命令(ifconfig/ethtool/nmon+n)

Linux网络常用命令ifconfig可设置网络设备的状态&#xff0c;或是显示目前的设置ethtool 是用于查询及设置网卡参数的命令服务端监控工具&#xff1a;nmonnmon可监控的数据类型ifconfig可设置网络设备的状态&#xff0c;或是显示目前的设置 显示linux系统中当前服务器中的全部…

【并发】并发锁机制-深入理解synchronized(二)

【并发】并发锁机制-深入理解synchronized&#xff08;二&#xff09; synchronized 高级篇&#xff08;底层原理&#xff09; synchronized是JVM内置锁&#xff0c;基于Monitor机制实现。 这个Monitor就是管程的意思&#xff0c;它可以控制线程&#xff0c;让其陷入等待&am…

想去看更大的世界,社科院与杜兰大学金融管理硕士项目给予你前行的勇气

当我们的工作生活趋于稳定&#xff0c;我们那颗不安分的心就按捺不住的跳动&#xff0c;想要去看更美的风景&#xff0c;探索更大的世界。所谓遥不可及的梦想才是你见过更大世界的证明&#xff0c;社科院与杜兰大学金融管理硕士项目给予你前行的勇气。一定要不断提高自己的认知…