• effective c++ 笔记 (18-22)






    //---------------------------15/04/06----------------------------


    //#18 让接口容易被正确使用,不易被误用

    {

    //  1:为了防止客户输入错误的参数,可以使用外覆类型来区别:

       struct Day

        {

           explicit Day(int d): val(d) {}

           int val;

        };


       struct Month

        {

           explicit Month(int m): val(m) {}

           int val;

        };


       struct Year

        {

           explicit Year(int y): val(y) {}

           int val;

        };


       class Date

        {

        public:

            Date(const Month& m,const Day& d, const Year& y);

            ...

        };


    //  这时,客户职能这么使用Date class

        Date d(Month(3), Day(30), Year(1995));


    //  为了限制类型的值,比如一年只有12个月,Month应该反映这样的事实。

    //  为了安全不直接使用enum,而是预先定义所有有效的Months

       class Month

        {

        public:

           static Month Jan() {return Month(1);}

           static Month Feb() {return Month(2);}

            ...

           static Month Dec() {return Month(12);}

        private:

           explicit Month(int m);

        };


        Date d(Month::Mar(), Day(30), Year(1995));


    /*  2:预防客户错误的另一个办法是:限制类型内什么事可做,什么事不能做。也就是加上const


        3:除非有好理由,否则应该尽量令你的types的行为与内置types一致


        4:任何接口如果要求客户必须记得做某些事情,就是有着不正确使用的倾向。

        较佳接口的设计原则是先发制人,比如条款13中,

        factory函数返回一个智能指针,可以避免客户忘记使用智能指针:                     */

        std::tr1::shared_ptr<Investment> createInvestment();

        

    //  5:tr1::shared_ptr支持定制型删除器,可以防范DLL问题:

        std::tr1::shared_ptr<Investment> createInvestment()

        {

            std::tr1::shared_ptr<Investment> retval(static_cast<Investment*>(0),

                                                    getRidOfInvestment);

            retval = ...;

           return retval;

        }

        

    }


    //#19   设计class犹如设计type

    {

    /*  1:type的对象应该如何被创建和销毁?

            这会影响到你的class的构造函数和析构函数,以及内存分配函数和释放函数。

            当然前提是如果你打算撰写他们。

        2:对象初始化和对象的赋值该有什么样的差别

            这个答案决定了构造函数和赋值操作符的行为,以及差异。

        3:type的对象如果被passed by value,意味着什么?

            copy构造函数用来定义一个typepass by value该如何实现。

        4:什么是新type合法值

            你的class必须维护自己的约束条件,也就决定了你的成员函数

            (特别是构造函数、赋值操作符、和所谓的setter函数)必须进行错误检查工作。

        5:你的新type需要配合某个继承图系吗?

            继承既有的class会受到哪些class的设计的束缚,特别是他们的函数是virtualnon_virtual的影响

            如果允许别人继承自己的类,就会影响你所声明的函数是否为virtual

        6:你的新type需要什么样的转换

            是否需要隐式转换、显式转换。

        7:什么样的操作符和函数对此新type而言是合理的?

            这个答案决定你为你的class声明哪些函数,其中某些该是member函数。

        8:什么样的标准函数应该驳回

            你不想要系统为你声明的函数要声明为private

        9:谁该取用新type的成员

            这个答案决定了哪个成员为publicprotectedprivate。以及哪个classfunction应该是friend

        10:什么是新type为声明接口

            它对效率、异常安全性以及资源运用提供何种保证

        11:你的新type有多么一般化

            看看自己是否应该定义一个新的class template

        12:你真的需要一个新type

            如果只是为既有的class添加机能,那么可能单纯定义一个或多个non member函数或templates

            更能达到目标。                         

    */

    }


    //#20   宁以pass by reference to const替换pass by value

    {

    /*  1:pass by value 需要调用copy构造函数产出一个副本,这可能使得pass by value

        成为昂贵的操作。如果传递的是一个自定义class,通常需要调用一次copy构造函数 一次析构函数

        如果这个class继承自别的类,又需要多调用好几次这两个函数。

        2:by reference方式传递参数可以避免slicing(对象切割)问题:

            一个derived class 对象以by value方式传递并被视为一个base class对象时,base class

            copy构造函数被调用,而造成此对象的行为属于derived class对象的部分被切掉了,只留

            下了一个base class对象。这绝不是你想要的。

        3:reference通常以指针来实现出来,所以pass by reference就相当于传递指针。因此如果是一些

        内置类型对象(比如int) pass by value往往比 pass by reference的效率高些。

        4:除了内置类型 stl的迭代器和函数对象,其他的对象都以pass by reference to const

        替换 pass by value

    */

    }


    //#21   必须返回对象时,别妄想返回其reference

    {

    //  1:一些值必须返回pass by value

    //      1>通过在stack上创建对象并返回这个对象的引用:

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

            {

                Rantional result(lhs.n * rhs.n, lhs.d * rhs.d);

               return result;

            }

    /*      这里有两个点:

                1)这样也调用构造了,效率并没提高。

                2)返回了一个已经被销毁的对象。严重的错误!

            2>通过在堆上创建对象并返回这个对象的引用:

                                                                    */

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

            {

                Rantional result =new Rantional(lhs.n * rhs.n, lhs.d * rhs.d);

               return result;

            }

    //      这样的话,谁负责delete?而且还是需要一次构造函数。

    //      3>使用static对象:

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

            {

               static Rantional result;

                result = ...;

               return result;

            }

    //      这样首先线程不安全,其次使用if((a * b) == (c * d))总是返回true

    //  2:当一个函数必须返回新对象时,就让那个函数返回一个新对象呗!

       inline const Rantionaloperator* (const Rantional& lhs,const Rantional& rhs)

        {

           return Rantional(lhs.n * rhs.n, lhs.d * rhs.d);

        }

    }


    //#22   将成员变量声明为private

    {

    /*  1:首先看看成员变量不该是public

            1>一致性:如果没有public成员变量,客户唯一能访问对象的办法就是使用成员函数

            客户就不需要在访问成员时疑惑地试着记住是否使用小括号。

            2>可以更加精准地控制成员变量:你可以通过函数控制成员变量的读写。

            3>封装性:如果通过函数访问成员变量,日后就算更改了这个变量的计算方法,或者直接更改了变量,

            客户也不知道,也不必知道。

            不封装就意味着不改变。

        2:成员变量不该是protected

            道理和public第三点一样,这样对derived class并没有封装性可言

     

    }






  • 相关阅读:
    Java自学-数组 创建数组
    Java自学-控制流程 结束外部循环
    Java自学-控制流程 break
    Java自学-控制流程 for
    Java自学-控制流程 continue
    Java自学-控制流程 switch
    Java自学-控制流程 If
    计算机组成原理之流水线处理器
    计算机组成原理之算术逻辑单元
    计算机组成原理之指令系统
  • 原文地址:https://www.cnblogs.com/boydfd/p/4983146.html
Copyright © 2020-2023  润新知