zoukankan      html  css  js  c++  java
  • Effective C++ 笔记(2)

    1.       继承与面向对象设计

    条款32: 确定你的public继承塑模出is-a关系

    条款33: 避免遮掩继承而来的名称

    无论是virtualnon-virtual, 如果derived class有复写,则屏蔽了virtual函数.编译器首先寻找derived作用域中函数名,再寻找base,如果找到则停止,并不判断参数.

     class base

    {

    public:

             virtual void mf1();

             virtual void mf1(int);

             virtual void mf2();

             void mf3();

             void mf3(int);

    };

    class derived: public base

    {

    public:

             virtual void mf1();

             void mf3();

             void mf4();

    }

    derived d;

    d.mf1(); //ok, derived::mf1;

    d.mf1(1); //error, derive::mf1() cover base::mf1()

    d.mf2(); //ok, base::mf2()

    d.mf3(); //ok, derived::mf3();

    d.mf3(1); //error, derive::mf3() cover base::mf3()

     

    derived d;

    base *p = &d;

    p->mf1(); // derived, base::mf1;

    p->mf3();//ok, base::mf3;

    这违背is-a关系,可以使用using声明

    class derived: public base

    {

    public:

             using base::mf1; /*base中名为mf1,mf3的所有东西在derived作用域中可见*/

             using base::mf3;

             virtual void mf1();

             void mf3();

             void mf4();

    };


    条款34: 区分接口继承和实现继承

     pure virtual: 只继承接口

    simple virtual: 继承接口和缺省实现

    non-virtual: 如果在derived class中重写则覆盖,所以不应覆写,代表的是不变性和凌驾特异性,继承接口和一份强制实现

    class airport{};

    class airplane

    {

    public:

             virtual void fly(const airport &);

    };

    class airplaneA: public airplan

    {

    //未定义fly函数,继承airplan::fly

    };

    class airplaneB: public airplan

    {

    //未定义fly函数,继承airplan::fly

    };

    如果derived有共同行为,可以将函数共同行为定义在base class中,随之而来的问题是,如果有增加一个derived class行为不同且未重定义函数,则造成错误.

    class airplaneC: public airplan

    {

    //未定义fly函数,继承airplan::fly,但是airplaneC fly方式不同,出错!!

    };

    所以需要将接口和缺省分开.

    class airplane

    {

    public:

             virtual void fly(const airport &) = 0;

    protected:

             void defaultfly(const airport &);

    };

    class airplanA: public airplan

    {

             virtual void fly(const airport &destination)

             {

                       defaultfly(destination);

             }

    };

    class airplanB: public airplan

    {

                       virtual void fly(const airport &destination)

             {

                       defaultfly(destination);

             }

    };

    class airplanC: public airplan

    {

                       virtual void fly(const airport &destination)

             {

                       //C own fly mode

             }

    };

    或者(个人喜欢后者)

    class airplane

    {

    public:

             virtual void fly(const airport &) = 0;

     

    };

    void airplane::fly(const airport &destination)

    {

             //default fly mode

    }

    class airplanA: public airplan

    {

             virtual void fly(const airport &destination)

             {

                       airplane::fly(destination);

             }

    };

    class airplanB: public airplan

    {

             virtual void fly(const airport &destination)

             {

                       airplane::fly(destination);

             }

    };

    class airplanC: public airplan

    {

             virtual void fly(const airport &destination)

             {

                       //C own fly mode

             }

    };

     

    条款35: 考虑virtual函数以外的其他选择

    NVI手法,non-virtual interface,这个思想流派主张virtual函数应该几乎总是private.而创建另一个non-virtual函数调用virtual函数,做一些事前工作或时候工作.

    class airplane

    {

    public:

             int fly()

             {

                       //...

                       dofly();

                       //...

             }

    private:

             virtual void dofly(const airport &) ;

    }

    另一种strategy

    class airplane;

    int defaultfly();

     class airplane

    {

    public:

             typedef void (*pflymodefunc)();

             explicit airplane(pflymodefunc ff = defaultfly) :flyfunc(ff)

             {

             }

             void fly() const

             {

                       flyfunc();

             }

    private:

             pflymodefunc flyfunc ;

    };

    class myairplane: public airplane

    {

             explicit airplaneA(pflymodefunc ff = defaultfly)

                       : airplane (ff)

             {

             }

     

    };

    void airplaneA_fly(void);

    void airplaneB_fly(void);

    myairplane planeA(airplaneA_fly); //不同的飞行方式

    myairplane planeB(airplaneB_fly); //不同的飞行方式

    借由tr1::function完成strategy模式

    class airplaneC_fly

    {

             void operator ()(void) const

             {

             }

    };

    class airplane

    {

    public:

             typedef std::tr1::function<void ()> pflymodefunc;

    /*void ()代表的是无参数,返回值为void的函数, tr1::function类型产生的对象可以持有任何遇刺签名式兼容的可调用物, 所为兼容,是指调用的变量可以隐式转换为参数类型,而实际返回值可以转换为返回值类型*/

             explicit airplane(pflymodefunc ff = defaultfly)

                       :flyfunc(ff)

             {

             }

             void fly() const

             {

                       flyfunc();

             }

    private:

             pflymodefunc flyfunc ;

    };

    void airplaneA_fly(void);

    myairplane planeA(airplaneA_fly); //不同的飞行方式

    myairplane planeC(airplaneC_fly()); //不同的飞行方式,函数对象

     

    古典的strategy模式

    将继承体系内的virtual函数替换为另一个继承体系内的virtual函数.

    class flymode

    {

             virtual void fly()

             {

             }

    };

    flymode defaultflymode;

    class airplane

    {

    public:

             typedef std::tr1::function<void ()> pflymodefunc;

     

             explicit airplane(flymode ff = defaultfly)

                       :pflymode(ff)

             {

             }

             void fly() const

             {

                       pflymode->fly();

             }

    private:

             flymode pflymode ;

    };

     

    条款36: 绝不重新定义继承而来的non-virtual函数

    对于动态绑定, 如果是virtual函数, 则现在derived class中寻找,如果没有寻到, 则在base class中寻找, 如果是non-virtual,则直接在base class中寻找.

    class base

    {

    public:

             virtual void func1();

             void func2();

    }

    class derived: public base

    {

    public:

             virtual void func1();

             void func2();

    }

    base *p = new derived();

    p->func1(); //derived::func1()

    p->func2(); //base::func2()

    delete p;

    derived d;

    d->func2(); //derived::func2()

     

    条款37: 绝不重新定义继承而来的缺省函数值

    virtual系数是动态绑定,而缺省函数值却是静态绑定.

    class base

    {

    public:

             virtual void func1(int i = 0);

             virtual void func2(int i = 0);

    };

    class derived: public base

    {

    public:

             virtual void func1(int i = 1);

    };

    class derived2: public base

    {

    public:

             virtual void func2(int i);

    };

    base *p = new derived();

    p->func1(); //derived::func1(0) , 动态绑定时, 默认参数从base继承

    delete p;


     *p = new derived2();

    p->func2(); //base::func2(0) , 动态绑定时, 默认参数从base继承

    delete p;

     

    derived2 d2;

    d2->func2(1); //静态绑定时, 不继承默认实参

    d2.func2(); /* error, 'derived::func2' : function does not take 0 arguments*/

     

    条款38: 通过复合塑模出has-a或根据某物实现出

    条款39: 明智而谨慎地使用private继承

    如果classes之间的继承关系是private, 编译器不会自动将一个derived class对象转换为一个base classs对象. private继承只是一种实现技术,private继承意味只有实现部分被继承,接口部分被略去.

    class base{};

    class derived: private base{};

    void func(const base&); //或者void func(const base)

    derived a;

    func(a); /* error, 'type cast' : conversion from 'derived *__w64 ' to 'const base &' exists, but is inaccessible*/

     

    private继承类似has-a, base的某些接口不想让用户使用或误用, 但需要重写某些virtual函数,使用private继承.

    class Timer

    {

    public:

             virtual void onTick(int i = 0); //call it once timer stricked

    };

    class myTimer: private Timer

    {

    private

    virtual void onTick(int i = 0);

    };

     

    可用复合代替

    class Timer

    {

    public:

             virtual void onTick(int i = 0); //call it once timer stricked

    };

    class myClass

    {

    private:

             class myTimer: public Timer

             {

             public:

                       virtual void func2(int i);

             }

             derived d;

    };

     

    如果class无成员变量, virtual function(vptr),无virtual base classed(这样的base class也会带来体积上的额外开销), 这样的empty class不使用任何空间, 但是C++判定,独立对象必须有非零大小.

    class empty

    {

    };

    classs derived{

    private:

             int x;

             empty e:

    }

    sizeof (derived) > sizeof(int);

    sizeof(empty) == 1 /*或者更大(齐位要求)*/

    C++会默默安插一个char到空对象内,但是这个约束不适合derived class对象内的base clas成分.

    classs empty_derived: private empty

    {

    private:

             int x;

    }

    sizeof(empty_derived) == sizeof(int); /*EBO(empty base optimization; 空白基类最优化, 一般只在单一继承下才可行*/

     

    条款40: 明智而谨慎地使用多重继承

     virtural继承的classs所产生的对象比使用non-virtual继承的class体积大,且访问成员的速度慢, 而且virtual base class的初始化由最底层derived class负责,如果非必要, 不要使用virtual继承.

    但是多重继承涉及 public继承某个interface class(pure virtual class), private继承某个写组实现的class的组合, 则正当.

     

    7. 模板与泛型编程

     

    条款41: 了解隐式接口和编译期多态

    条款42: 了解typename的双重意义.

    typename比较class多了一个意义: 声明是嵌套从属名称.

    template<typename T>void func(ITerT iter)

    {

             C::const_iterator *x; /*编译期认为C::const_iterator不是类型而是变量, 如果C::const_iterator是一个变量,*代表乘号.*/

    typename C::const_iterator *x; //ok

    typename  std::iterator_traits<ITerT>::value_type temp(iter); /*代表对象所指物的类型,如果ITerTvector<int>iterator,temp就是int*/

    }

    template不可再base classes list,也不可再member initialization list中作为base class 修饰符.

     

    条款43: 学习处理模板化基类内的名称

    template<typename T> class base

    {

             void func();

    }

    template<typename T> class derived: public base<T>

    {

             void func2()

             {

                       func(); /*不能通过编译,有可能基类被特例化而无func函数,所以func对编译器不可见.*/

             }

    }

    template<c> class base<classtype>

    {

    //func函数.

    }

    需要在derived中使用using声明

    template<typename T> class derived: public base<T>

    {

    using base<T>::func;

             void func2()

             {

                       func(); //不能通过编译,有可能基类被特例化而无func函数.

             }

    }

    或者

    template<typename T> class derived: public base<T>

    {

             void func2()

             {

                       this->func(); //不能通过 ?,有可能基类 被特例化而无函数.

             }

    }

     

    derived<classtype> d;

    d.func2(); //编译错误.

     

    条款44: 将与参数无关的代码抽离templates

    条款45: 运用成员函数模板接受所有兼容类型

    智能指针

    template<typename T>class smrtptr

    {

    public:

    template<typename U>smartptr(const SmartPtr<U> &other): ptr(other.get())

             {

             }

    /*编译器会自动生成copy构造函数smartptr(smart const&)*/

             explicit smartptr<T *other>;

             //...

             T *get() const

             {

                       return ptr;

             }

    private:

             T *ptr;

    };

    smartptr<base> ptr = smart<derived>(new derived);

     

    条款46: 需要类型转换时请为模板定义非成员函数

    template<typename T>class myclass

    {

    public:

             myclass(const T &n = 0, const T &d = 1);

    };

    template<typename T> const myclass<T> operator *(const myclass<T> &lhs, const myclass<T> &rhs)

    {

    }

    myclass<int> onehalf(1, 2);

    myclass<int>result = onehalf * 2; /* error, 'const myclass <T> operator *(const empty<T> &,const empty<T> &)' : could not deduce template argument for 'const empty<T> &' from 'int' , template无法通过构造函数隐式进行隐式类型转化, 所以无法将2转换为myclass<int>从而将T推断为int*/

     

    template<typename T>class empty

    {

    public:

             empty(const T &n = 0, const T &d = 1){}

             friend

             const empty operator *(const empty &lhs, const empty &rhs); /*被声明却没被定义.*/

    };

    template<typename T> const empty<T> operator *(const empty<T> &lhs, const empty<T> &rhs)

    {

    }

    empty <int> onehalf(1, 2);

    empty <int>result = onehalf * 2; /*无法link, error LNK2028: unresolved token (0A00029C) "class empty<int> const __cdecl operator*(class empty<int> const &,class empty<int> const &)" (??D@$$FYA?BV?$empty@H@@ABV0@0@Z) referenced in function "int __cdecl main(int,char * * const)" (?main@@$$HYAHHQAPAD@Z)*/

    由于onehalf被声明为empty<int>class empty<T>被具化, friend函数operator *也被自动声明, 所以编译其可以使用构造函数进行隐式转换.

     

    template<typename T>class empty

    {

    public:

             empty(const T &n = 0, const T &d = 1){}

             friend

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

    {

    }

    };

    empty <int> onehalf(1, 2);

    empty <int>result = onehalf * 2; //ok

     

    条款47: 请使用traits classed表现类型信息

    条款48: 认识template元编程

     

    8 定制newdelete

    条款49: 了解new-handler的行为

    operator new抛出异常以反映一个未获满足的内存需求之前, 它会先调用一个客户指定的错误处理函数, 一个所谓的new-handler, 调用set_new_handler函数, 声明于<new>的一个标准程序库函数.

    namespace std

    {

             typedef void (*new_handler)();

             new_handler set_new_handler(new_handler p) throw(); /*返回旧的handler*/

    }

    class专属new-handler

    class myclass

    {

    public:

             static std::new_handler set_new_handler(std::new_handler p) throw();

             static void* operator new(std::size_t size) throw(std::bad_alloc);

    /*调用set_new_handler, call global operator new, 分配失败,则会调用new_handler, 如果最终无法分配足够内存, 抛出bad_alloc, 此情况下, 必须确保原本的new_handler恢复, 如果分配成功, 返回指针, 析构函数需要将原本的handler恢复*/

    private:

             static std::new_handler currenthandler;

    };

    static std::new_handler myclass ::currenthandler = NULL;

    std::new_handler myclass::set_new_handler(std::new_handler p) throw()

    {

             std::new_handler oldhandler = currenthandler;

             currenthandler = p;

             return oldhandler;

    }

    void* myclass::operator new(std::size_t size) throw(std::bad_alloc)

    {

             newhandlerholder(std::set_new_handler(currenthandler));

             return std::operator new(size);

    }

     

    下列class, newhandlersupport, 虽然没有用到T, 但是使用template是为了让每一个拥有异体的newhandersupport(更确切地说是currenthandler)

    template<typename T>

    class newhandlerholder

    {

    public:

             explicit newhandlerholder(std::new_handler):handler(nh){}

             ~newhandlerholder(){std::set_new_handler(handler)}

     

    private:

             std::new_handler handler;

             newhandlerholder(const newhandlerholder&);

             newhandlerholder& operator=(newhandlerholder &);

    };

    template<typename T>

    class newhandlersuport

    {

    public:

             static std::new_handler set_new_handler(std::new_handler p) throw();

             void* operator new(std::size_t size) throw(std::bad_alloc)

             {

                       newhandlerholder(std::set_new_handler(currenthandler));

                       return std::operator new(size);

             }

    private:

             static std::new_handler currenthandler;

    };

    template<typename T> std::new_handler newhandlersuport<T>::currenthandler = NULL;

    template<typename T> std::new_handler newhandlersuport<T>::set_new_handler(std::new_handler p) throw()

    {

             std::new_handler oldhandler = currenthandler;

             currenthandler = p;

             return oldhandler;

    }

    class empty: public newhandlersuport<empty>

    {

    };

     

    9: 杂项讨论

    条款53: 不要轻忽编译器的警告

    条款54: 让自己熟悉包括TR1在内的标准程序库

    条款55: 让自己熟悉Boot

  • 相关阅读:
    myeclipse中代码不显示SVN版本号
    java HttpURLConnection 登录网站 完整代码
    新浪微博自动(模拟)登陆详解及实现
    java模拟Cookies登陆
    paper 53 :深度学习(转载)
    paper 52 :windows7环境下theano安装
    paper 51:图像复原
    paper 50 :人脸识别简史与近期进展
    paper 49:论文退稿?审稿人帮你总结了22个能避免的常见问题
    paper 48: Latex中如何制作参考文献
  • 原文地址:https://www.cnblogs.com/zengyou/p/2195595.html
Copyright © 2011-2022 走看看