• effective c++ 笔记 (23-25)



    //---------------------------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绝对不能抛出异常。              

    */

    }



  • 相关阅读:
    asp.net mvc上传头像加剪裁功能
    CSS基础部分总结
    Web前端之初学CSS
    JavaScript中的new操作符的原理解析
    JS常用数组API汇总
    【NOIP2020提高B组模拟11.26】卡常题
    【NOIP2020提高B组模拟11.26】逗气
    CSP-J/S总结
    计数题
    浅谈线段树——基础篇
  • 原文地址:https://www.cnblogs.com/boydfd/p/4983143.html
Copyright © 2020-2023  润新知