• effective c++ 笔记 (41-44)





    //---------------------------15/04/25----------------------------


    //#41   了解隐式接口和编译期多态

    {

    //  1:面向对象编程总是以显示接口和运行期多态解决问题:

        void doProcessing(Widget& w)

        {

            if(w.size() > 10 && w != someNastyWidget)

            {

                Widget temp(w);

                temp.normalize();

                temp.swap(w);

            }

        }

    /*      1>w的类型被声明为Widget,所以w必须支持Widget接口,我们可以从源码中找出这个接口,看看

              时什么样子的,这种接口称之为显式接口。

            2>对于virtual函数,w将在运行期表现出多态。

        2:Template的世界与面向对象不同,隐式接口和编译期多态变得很重要:                   */

        

        template<typname T>

        void doProcessing(T& w)

        {

            if(w.size() > 10 && w != someNastyWidget)

            {

                T temp(w);

                temp.normalize();

                temp.swap(w);

            }

        }

    /*      1>w必须支持哪一种接口是由执行于w身上的操作来决定的,比如上面看起来w必须支持size

              normalizeswapcopy构造函数,不等于比较等接口。这些便是隐式接口。

            2>凡是涉及w的任何函数调用,有可能造成template具现化行为,这样的行为发生在编译期。

              ”以不同的template参数具现化function template“ 会导致调用不同的函数,这就是编译

              期多态。

        3:再探隐式接口:

            1>T并不需要提供一个名为size的成员函数,因为T有可能从base class继承而得。

            2>T并不需要支持operator!=,因为有可能有个operator!=函数,接受类型为X的对象和类型

              Y的对象,而T可以被转换为XsomeNastyWidget可以被转换为Y

    */

    }


    //#42   了解typename的双重意义

    {

    /*  1:在声明template的时候,classtypename完全一样。

            template<class T> 等价于 template<typename T>

        2:当需要用到T中的类型时,需要明确指定是一个类型,也就是在前面加上一个typename:

            typename T::const_iterator iter;

          如果不指定的话,编译器时无法知道这是一个类型还是一个成员变量的,下面的语句可以造成歧义:

            T::const_iterator* pIter;

          到底是声明一个指针变量还是拿T中的const_iterator pIter相乘呢?编译器不知道!

        3:不允许使用typename的地方:

            1>typename T t;  这样是错误的,typename知识用来验证嵌套从属类型名称的,前面那句声明

              编译器是很明确知道的,不需要加typename

            2>typename不能出现在base class list member.init.list                  */

        template<typename T>

        class Derived: public Base<T>::Nested           //不需要typename 编译器知道的

        {

        public:

            explicit Derived(int x) : Base<T>::Nested(x)    //不需要使用typename

            {

            }

        }

    }


    //#43   学习处理模版化基类内的名称

    {

    /*  如果基类是一个模版类,子类继承这样的基类时,如果想要调用基类的函数必须使用以下三种方法之一,否则

        无法通过编译,因为基类是有可能特化的,所以编译器无法确定基类中是否真的存在你想调用的函数。比如你想

        调用基类的sendClear:

            1>使用this来调用:

                this->sendClear();

            2>使用using声明式:

                using Base<T>::sendClear;

                sendClear();

            3>指明被调用的函数位于base(不推荐使用这种方法)

                Base<T>::sendClear()

              不推荐的理由是,这种调用没有多态可言,如果sendClear是呈现多态,那么这里只会调用基类的函数。

              但是换个思维来想,如果是virtual函数,那么子类中应该有相应实现吧。就不用别的操作,直接调用了。

              (也就是一开始就通过编译了,不会考虑着三种方法了)

    */

    }


    //#44   将与参数无关的代码抽离templates

    {

    /*  1:使用template可以节省时间和避免代码重复,然而,看起来简洁的代码有可能会使代码的二进制码重复

          而造成代码膨胀。

        2:如何优化:

            1>抽离相同的东西放入模版基类中。比如抽离相同的函数,相同的成员变量等。

            2>当有需要时,可以给基类配置一个成员变量,抽离的函数 需要使用到的 一个指向子类中数据 的指针。

        3:虽然做了这么多优化,但是需要付出一定的代码,简单来说就是执行速度。

          这样的代码局部性不强,所以高速缓存很有可能无法命中。导致效率下降。

        4:还有导致代码膨胀的情况:

            1>intlong在很多平台上有相同的二进制表示,所以list<int>,list<long>实现的版本的二进制

              表示是一样的,但是却实现了两份一样的代码。

            2>所有的指针,比如list<const int*>, list<int>, list<Aclass*>这些底层实现应该唯一。

              可以通过特化list<T*>来防止膨胀。

        5:第一情况造成的膨胀应该考虑空间和时间,从中选择一个。

          后面两种膨胀的情况时可以避免的。

    */

    }








  • 相关阅读:
    TransactSQL语言 学习sql server2005 step by step(四)
    一步一步学习C#(一)
    SQL实例进阶学习sql server2005 step by step(八)
    SQL Server中常用全局变量和函数 学习sql server2005 step by step(五)
    SQL实例进阶学习sql server2005 step by step(七)
    C#操作excel(开篇)
    SQL进阶提升(平时小积累)学习sql server2005 step by step(十)
    mysql 备份各种方法
    ubuntu 这可怕的弹出窗口啊“Enter password to unlock your login keyring”
    很简单的内核模块A+B
  • 原文地址:https://www.cnblogs.com/boydfd/p/4983123.html
Copyright © 2020-2023  润新知