effective c++ 笔记 (7)


//---------------------------15/04/08----------------------------

//#23   宁以non_membernon_friend替换member函数

{

    class WebBrowser

    {

    public:

        ...

        void clearCache();

        void clearHistory();

        void removeCookies();

        ...

    };

    

/*  对于上面这个函数,有的客户会想一次调用这三个函数,那么时使用member函数好呢,

    还是non_menber non_friend函数好呢

    1:对于封装性来说,是non_member版本比较好。

        1>从封装开始讨论:如果某东西被封装,它就不再可见。愈多东西被封装,愈少人可以看到它。

          而愈少人看到它,就有愈大的弹性去变化它,因为我们的改变仅仅直接影响看到改变的那些人事物。

          就这一点来说,能够访问private成员变量的就是成员函数和friend函数。如果non_member

          member函数提供的功能是相同的,那么,封装性较大的是non_member函数,因为它不增加能访问

          classprivate成分的数量。

        2>member函数说的只是针对能访问到private成员的类,并不是说这个函数不能是别的类的member函数。

    2:把这些non_member函数放入namespace并分类到不同的头文件中。这么做的原因是,客户可以针对不同的

      功能,包含不同的头文件。

*/

}


//#24   若所有参数皆需类型转换,请为此采用non_member函数

{

//  class支持隐式类型转换通常是个糟糕的主意。然而也有例外,最常见的例外是在建立数值类型时。

//  如果要设计一个有理数的类,让他支持int隐式转换到有理数还是很合理的

    class Rational

    {

    public:

        Rational(int numerator = 0, int denominator = 1)//没有使用explicit,允许隐式转化

        int numerator() const;

        int denominator() const;

    private:

    };

//  让有理数类支持+ * 等操作时,使用non_member函数实现,类内部的operatpr*()操作无法实现全部功能。

    const Rational operator*(const Rational& rhs) const;

    

    Rational oneHalf(1,2),result;

    result = oneHalf * 2;   //可以正确调用operator*()操作,2 在参数列表中,可以隐式转换

    result = 2 * oneHalf;   //错误,没有这样的操作

    

//  结论:只有当参数被列于参数列表内,这个参数才是隐式类型转换的合格参与者。

//  所以,只能使用non_member函数,把两个数都放入参数列表中

    const Rational operator*(const Rational& lhs, const Rational& rhs)

    {

        return Rational(lhs.numerator() * rhs.numerator(),

                        lhs.denominator() * rhs.denominator());

    }

//  是否让它成为friend的? 没有必要,不是friend就能完成任务,就不必成为friend

}


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

{

//  swap原本是stl的函数:

    namespace std

    {

        template<typename T>

        void swap(T& a, T& b)

        {

            T temp(a);

            a = b;

            b = temp;

        }

    }

//  这种调用方式,在某些情况,会产生不必要的消耗:

    class WidgetImpl

    {

    public:

        ...

    private:

        int a,b,c;

        stad::vector<double> v; //意味着复制需要的时间很长

    };

    

    class Widget

    {

    public:

        Widget(const Widget& rhs);

        Widget& operator=(const Widget& rhs)

        {

            ...

            *pImpl = *(rhs.pImpl);  //深拷贝

            ...

        }

    private:

        WidgetImpl* pImpl;

    };

    

//  当执行swap的时候,正常情况应该直接交换两个 pImpl的指针,而stl版本的却要复制三个widgetImpl对象

//  为了特化,可以声明一个成员函数对指针调用swap函数:

    class Widget

    {

    public:

        void swap(Widget& other)

        {

            using std::swap;

            swap(pImpl, other.pImpl);

        }

        ...

    };

    

    namespace std

    {

        template<>

        void swap<Widget>(Widget& a, Widget& b)

        {

            a.swap(b);

        }

    }

//  Widget时个class template时,这么做是错的:

    namespace std

    {

        template<typename T>

        void swap< Widget<T> >(Widget<T>& a, Widget<T>& b)

        {

            a.swap(b);

        }

    }

//  这样是在偏特化 swap的函数模版参数,并不是偏特化swap函数,所以可以使用重载:

    

    namespace std

    {

        template<typename T>

        void swap(Widget<T>& a, Widget<T>& b)

        {

            a.swap(b);

        }

    }

//  然而我们不能往std中添加新的东西。所以我们可以把non_member函数放到Widgetnamespace那儿

    namespace WidgetStuff

    {

        template<typename T>

        class Widget{...};

        ...

        template<typename T>

        void swap(Widget<T>& a, Widget<T>& b)

        {

            a.swap(b);

        }

    }

//  使用

    template<typename T>

    void doSomething(T& obj1, T& obj2)

    {

        using std::swap;        //为了stdswap可见,至于调不调用是编译器的事

        ...

        swap(obj1, obj2);

    }

/*  swap总结:

        1>提供一个public swap成员函数,让它高效地置换你的类型的两个对象值。

        2>在你的classtemplate所在的命名空间内提供一个non_member swap,并令它调用上述swap成员函数

        3>如果你编写的class不是class template。为你的class特化std::swap,并令让调用swap成员函数。

        4>在使用swap时,记得使用 using std::swap,使得std::swap可见。

        5>成员版swap绝对不能抛出异常。              

*/

}



郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。