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


    我们可以调用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++ 标准委员会定义的,为了方便别人的使用,咱们还是放在咱们自己定义的空间中吧。


    现在的考虑另一种情况:假如你在一个函数模板中需要调用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完成。

  • 相关阅读:
    set 用法、小结
    AC 自动机优化
    HDU 2222 Keywords Search 【ac自动机】
    组合数学 隔板法
    BZOJ1303_中位数图_KEY
    初识Trie_对Trie的一些认识
    网络流Edmonds-Karp算法入门
    Codevs1332_上白泽慧音_KEY
    Fliptil_KEY
    2017Noip普及组游记
  • 原文地址:https://www.cnblogs.com/wuchanming/p/3757328.html
Copyright © 2020-2023  润新知