Effective C++条款25:考虑写出一个不抛出异常的swap

转载自:https://www.cnblogs.com/wuchanming/p/3757328.html

我们可以调用std下的swap函数,这是一个模板函数:既可以:

    int a = 1;
    int b = 2;
    std::swap(a,b);
    cout<<"a = "<<a<<" b = "<<b<<endl;

也可以(前提这个类型支持复制构造函数和赋值构造函数):

class Test
{
public:
    Test(int i):val(i){}
    int getVal(){return val;}
private:
    int val;
};

    Test a(1);
    Test b(2);
    std::swap(a,b);
    cout<<"a = "<<a.getVal()<<" b = "<<b.getVal()<<endl;
    return 0;

 

这个函数是是同通过类似int tmp = a; a = b; b = tmp的方法实现的,所以,如果类中的数据成员较多,这样的交换缺乏效率。
相比之下,“以指针指向一个对象,内含真正的数据”的方法更受欢迎。比如pimpl(pointer to implementation)。然后交换它们的指针。按照这种方法,我们应该这样设计我们的类:

//类的具体实现
class TestImpl
{
public:
    TestImpl(int i):ival(i){}
    int getVal(){return ival;}
    
private:
    int ival;
};

//指针
class Test
{
public:
    Test(int i):p(new TestImpl(i)){}
    ~Test(){delete p;}
    Test operator=(const Test rhs)
    {
        *p = *(rhs.p);
    }
    int getVal()
    {
        return this->p->getVal();
    }
    void swap(Test& other)
    {
        using std::swap;
        swap(p,other.p);
    }
private:
    TestImpl *p;
};

 

我们在Test类中,同过调用std::swap完成了指针的交换。为了是得我们的swap更像是std中的swap函数,我们将std中的swap特化

namespace std
{
    template<>
    void swap<Test>(Test &a,Test &b)
    {
        a.swap(b);
    }
}

 

特化版本调用的就是类成员函数中的swap。

但是,如果Test和TestImpl都是类模板

//类的具体实现
template <typename T1>
class TestImpl
{
public:
    TestImpl(T1 i):ival(i){}
    virtual T1 getVal(){return ival;}
    
private:
    T1 ival;
};

//指针
template <typename T1>
class Test
{
public:
    Test(T1 i):p(new TestImpl(i)){}
    ~Test(){delete p;}
    Test operator=(const Test rhs)
    {
        *p = *(rhs.p);
    }
    T1 getVal()
    {
        return this->p->getVal();
    }
    void swap(Test& other)
    {
        using std::swap;
        swap(p,other.p);
    }
private:
    TestImpl *p;
};

 

那么我们似乎需要这么改写原先的交换函数:

namespace std
{
    template<typename T1>
    void swap<Test<T1>>(Test<T1> &a,Test<T1> &b)
    {
        a.swap(b);
    }
}

 

但这并不合法,因为C++规定,不能偏特化一个函数模版。而这里却将swap的类型特化为了Test<T1> &。

有没有好的办法呢?其实很简单,只要把原来的函数重载就行了

  template<typename T>
    void swap(Test<T>& a, Test<T>& b)
    {
        a.swap(b);
    }

剩下的问题就是吧这个函数放在哪里了?首先,设为全局函数是非常不好的,因为我们很有可能会经常调用swap函数的“平凡”版本。所以放在命名空间中是一 个不错的选择。但是有两点需要注意:1.这个命名空间中也必须包括我们定义的模板类。2.不要放在std空间内。虽然放进去也能使用,但是std是C++ 标准委员会定义的,为了方便别人的使用,咱们还是放在咱们自己定义的空间中吧。

namespace WidgetStuff{
    ...
    
    template <typename T1>
    class TestImpl{.......};

    template<typename T1>
    class Test{.......};
    ...
    template <typename T>
    void swap(Test<T> & a, Test<T> & b)
    {
        a.swap(b);
    }
}


现在的考虑另一种情况:假如你在一个函数模板中需要调用swap函数,该怎么做呢?首先,你希望的情况是:最好调用专属的swap,如果不存在,那么调用std下的swap:

template <typename T>
void doSomething(T& obj1, T& obj2)
{
    //其他操作省略
    using std::swap;
    swap(obj1,obj2);
}

那么,根据名字查找规则,则会通过argument-dependent look先找出命名空间内的重载的swap,如果找不到,则再调用std内的。这里的using声明的作用只是让这个函数“曝光”,至于用不用,则是另一 码事。但是如果你写成了using std::swap(obj1,obj2);就表明你肯定是要用std下的swap了。
总而言之:
1.虽然std下提供了一个swap函数,但是由于这个函数效率不高,所以我们倾向于在交换类时,通过pimpl技术交换指针。
2.首先,我们需要定义一个public swap函数,在这个函数中调用std下的swap函数完成指针的交换。
3.然后定义一个非成员swap函数,这个函数调用public swap。
4.假如的是类模板,那么,需要重载一个swap函数,然后将这个函数与模板类一起放在一个命名空间中。
5.在一个函数模板中需要调用swap函数时应使用using std::swap;声明。这样对于与重载的swap函数类型不符的函数,会调用std下的swap完成。

注意,成员版本的swap绝对不能抛出异常,但是非成员版本的swap函数是允许抛出异常的。前者不抛出异常的原因是其往往是针对内置类型的操作,而内置类型的操绝对不允许抛出异常。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
资源包主要包含以下内容: ASP项目源码:每个资源包中都包含完整的ASP项目源码,这些源码采用了经典的ASP技术开发,结构清晰、注释详细,帮助用户轻松理解整个项目的逻辑和实现方式。通过这些源码,用户可以学习到ASP的基本语法、服务器端脚本编写方法、数据库操作、用户权限管理等关键技术。 数据库设计文件:为了方便用户更好地理解系统的后台逻辑,每个项目中都附带了完整的数据库设计文件。这些文件通常包括数据库结构图、数据表设计文档,以及示例数据SQL脚本。用户可以通过这些文件快速搭建项目所需的数据库环境,并了解各个数据表之间的关系和作用。 详细的开发文档:每个资源包都附有详细的开发文档,文档内容包括项目背景介绍、功能模块说明、系统流程图、用户界面设计以及关键代码解析等。这些文档为用户提供了深入的学习材料,使得即便是从零开始的开发者也能逐步掌握项目开发的全过程。 项目演示与使用指南:为帮助用户更好地理解和使用这些ASP项目,每个资源包中都包含项目的演示文件和使用指南。演示文件通常以视频或图文形式展示项目的主要功能和操作流程,使用指南则详细说明了如何配置开发环境、部署项目以及常见问题的解决方法。 毕业设计参考:对于正在准备毕业设计的学生来说,这些资源包是绝佳的参考材料。每个项目不仅功能完善、结构清晰,还符合常见的毕业设计要求和标准。通过这些项目,学生可以学习到如何从零开始构建一个完整的Web系统,并积累丰富的项目经验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值