C++继承与面向对象 Effective C++总结(item 32 - 39)

    1. public继承试图模塑成一种 is-a的关系,需要保证设计出的继承关系满足 is-a关系。(item 32
    2. Avoid hiding inherited names item 33
      1. 派生类的作用域嵌于基类中
      2. 这意味着,定义于派生类中的同名名字会遮掩继承而来的名字。

    例子:

    class Base { 

    private: 

        int x; 

    public: 

        virtual void mf1() = 0; 

        virtual void mf1(int); 

        virtual void mf2(); 

        void mf3(); 

        void mf3(double); 

    }; 

    class Derived : public Base { 

    public: 

        virtual void mf1(); 

        void mf3(); 

        void mf4(); 

    };

    以作用域为基础的“名称遮掩规则”并没有改变,因此base class内所有名为mf1和mf3的函数都被derived class内的mf1和mf3函数遮掩掉了。

    Derived d; 

    int x; 

    d.mf1(); 

    d.mf1(x);//错误,Derived::mf1遮掩了Base::mf1 

    d.mf2(); 

    d.mf3(); 

    d.mf3(x);//错误,Derived::mf3遮掩了Base::mf3

    即使base class和derived classes内的函数有不同的参数类型也适用,而且不论函数是virtual或non-virtual也适用

     

    重要 (来自primer 的解释 page 549名字查找先于类型检查

    在调用d.mf1(x);时,

    1. 编译器先在派生类中寻找名字mf1
    1. 然后找到后就不会再找了,然后进行类型检查
    2. 发现mf1int),没有这样的原型函数,所以报错。
    1. 那么怎么办?
      1. 使用using

    不幸的是,你通常会想继承重载函数。实际上你在使用public继承而不继承那些重载函数,就是违反basederived classes之间的is-a关系,而is-apublic继承的基石。

    你可以用using声明式达成目标:

    class Derived : public Base { 

    public:

    //base class内的public名称在publicly derived class内也应该是public。 

        using Base::mf1;    // base class内为mf1mf3的所有东西 

        using Base::mf3;    //Derived class作用域内都可见(并且public) 

        virtual void mf1(); 

        void mf3(); 

        void mf4(); 

    };

    Derived d; 

    int x; 

    d.mf1(); 

    d.mf1(x);//现在没问题了,调用Base::mf1 

    d.mf2(); 

    d.mf3(); 

    d.mf3(x);//现在没问题了,调用Base::mf3

    这意味着如果你继承base class并加上重载函数,而你又希望重新定义或覆写(推翻)其中一部分,那么你必须为那些原本会被覆盖的每一个名称引入一个using声明式,否则某些你希望继承的名称会被覆盖。

    1. 使用转交函数

    然而在private继承之下(条款39)却可能是有意义的。如果Derived唯一想继承的mf1是那个无参数版本。using在这里派不上用场,using会令继承而来的某给定名称之所有同名函数在Derived class中都可见。我们需要一个简单的转交函数(forwarding function):

    class Base { 

    private: 

        int x; 

    public: 

        virtual void mf1() = 0; 

        virtual void mf1(int); 

        virtual void mf2(); 

        void mf3(); 

        void mf3(double); 

    };

    class Derived : private Base { 

    public: 

        virtual void mf1()//转交函数 

       

            Base::mf1();//暗自转成inline 

        }

    … 

    };

    Derived d; 

    int x; 

    d.mf1();//调用的是Derived::mf1 

    d.mf1(x);//错误,Base::mf1()被遮掩了

    1. 接口继承和实现继承 item 34

    我的理解:

    所谓接口,就是一个类面向它的客户暴露出来的可调用的部分(比如函数和属性)

    而一个派生类在继承基类的时候,是作为基类的客户存在的,所以派生类能够继承的也就是基类的publicprotected成员(public继承)。

    所以

    1. public继承,派生类总是继承基类的接口。
    2. 为什么不说也继承了基类的实现呢,是因为当基类中的函数数虚函数的时候
      1. 如果是纯虚函数,那么不能够继承实现,派生类必须提供自己的实现。
      2. 如果是普通的虚函数,那么可以继承实现,也可以覆盖默认实现
    3. 而如果函数是非虚函数,那么派生类总是继承了接口以及实现。

     

    基类也可以定义自己的纯虚函数的实现。

    1. item 35 还不能很好的理解
    2. 不要重新定义继承而来的非虚函数 item 36

    因为会覆盖继承而来的名字,导致结果和想要的不一致。(不是 is-a 关系)

    1. 不要重新定义继承而来的缺省参数值 item 37

    一句话:virtual 函数是动态绑定,而缺省参数却是静态绑定的。

    1、对象的静态类型:对象在声明时采用的类型。是在编译期确定的。

    2、对象的动态类型:目前所指对象的类型。是在运行期决定的。对象的动态类型可以更改,但是静态类型无法更改。

    关于对象的静态类型和动态类型,看一个示例:

    [cpp] 

    view plain 

    copy

    1. class B  
    1. {  
    2. }  
    1. class C : public B  
    1. {  
    2. }  
    1. class D : public B  
    1. {  
    2. }  
    3. D* pD = new D();//pD的静态类型是它声明的类型D*,动态类型也是D*  
    4. B* pB = pD;//pB的静态类型是它声明的类型B*,动态类型是pB所指向的对象pD的类型D*  
    5. C* pC = new C();  
    6. pB = pC;//pB的动态类型是可以更改的,现在它的动态类型是C*  

    3、静态绑定:绑定的是对象的静态类型,某特性(比如函数)依赖于对象的静态类型,发生在编译期。

    4、动态绑定:绑定的是对象的动态类型,某特性(比如函数)依赖于对象的动态类型,发生在运行期。

    [cpp] 

    view plain 

    copy

    1. class B  
    1. {  
    2.     void DoSomething();  
    3.     virtual void vfun();  
    4. }  
    1. class C : public B  
    1. {  
    2.     void DoSomething();//首先说明一下,这个子类重新定义了父类的no-virtual函数,这是一个不好的设计,会导致名称遮掩;这里只是为了说明动态绑定和静态绑定才这样使用。  
    3.     virtual void vfun();  
    4. }  
    1. class D : public B  
    1. {  
    2.     void DoSomething();  
    3.     virtual void vfun();  
    4. }  
    5. D* pD = new D();  
    6. B* pB = pD;  

    让我们看一下,pD->DoSomething()和pB->DoSomething()调用的是同一个函数吗?

    不是的,虽然pD和pB都指向同一个对象。因为函数DoSomething是一个no-virtual函数,它是静态绑定的,也就是编译器会在编译期根据对象的静态类型来选择函数。pD的静态类型是D*,那么编译器在处理pD->DoSomething()的时候会将它指向D::DoSomething()。同理,pB的静态类型是B*,那pB->DoSomething()调用的就是B::DoSomething()。

    让我们再来看一下,pD->vfun()和pB->vfun()调用的是同一个函数吗?

    是的。因为vfun是一个虚函数,它动态绑定的,也就是说它绑定的是对象的动态类型,pB和pD虽然静态类型不同,但是他们同时指向一个对象,他们的动态类型是相同的,都是D*,所以,他们的调用的是同一个函数:D::vfun()。

    上面都是针对对象指针的情况,对于引用(reference)的情况同样适用。

    指针和引用的动态类型和静态类型可能会不一致,但是对象的动态类型和静态类型是一致的。

    D D;

    D.DoSomething()和D.vfun()永远调用的都是D::DoSomething()和D::vfun()。

    至于那些事动态绑定,那些事静态绑定,有篇文章总结的非常好:

    我总结了一句话:只有虚函数才使用的是动态绑定,其他的全部是静态绑定。目前我还没有发现不适用这句话的,如果有错误,希望你可以指出来。

    特别需要注意的地方

    当缺省参数和虚函数一起出现的时候情况有点复杂,极易出错。我们知道,虚函数是动态绑定的,但是为了执行效率,缺省参数是静态绑定的。

    [cpp] 

    view plain 

    copy

    1. class B  
    1. {  
    2.  virtual void vfun(int i = 10);  
    3. }  
    1. class D : public B  
    1. {  
    2.  virtual void vfun(int i = 20);  
    3. }  
    4. D* pD = new D();  
    5. B* pB = pD;  
    6. pD->vfun();  
    7. pB->vfun();  

    有上面的分析可知pD->vfun()和pB->vfun()调用都是函数D::vfun(),但是他们的缺省参数是多少?

    分析一下,缺省参数是静态绑定的,pD->vfun()时,pD的静态类型是D*,所以它的缺省参数应该是20;同理,pB->vfun()的缺省参数应该是10。编写代码验证了一下,正确。

    对于这个特性,估计没有人会喜欢。所以,永远记住:

    “绝不重新定义继承而来的缺省参数(Never redefine function’s inherited default parameters value.)”

     

    来自 <http://blog.csdn.net/chgaowei/article/details/6427731>

     

    1. 复合(composition

    也就是一个类作为另外一个类的成员,通过这样的组合方式模塑出的是一种 has-a关系(从用户看来),或者 is-implemented-in-terms-of的关系(从实现上看来)。

    1. 关于private继承 item 39
      1. 含义:
        1. private继承意味着  is-implemented-in-terms-of,基类和派生类之间没有任何观念上的关系,仅仅是一种实现技术‘
        2. 也就是private继承意味着,只有实现被继承,接口部分应该略去。
      2. 和复合的选择

    下面总结自:https://isocpp.org/wiki/faq/private-inheritance 以及 item 39

    1. 一般来说都应该使用复合,除非
    2. 需要访问protected成员,或者
    3. 需要重新定义继承而来的虚函数,或者
    4. 对空类空间的优化。
    1. private继承的转型问题

    private派生类的对象指针(或引用)能否从派生类向基类转型:

    对于在某个给定节点来说,如果基类的共有成员(接口)是可访问的,则派生类能够向基类转型。这就说明:

    1. 在派生类的函数或者友元内,是可以执行转换的。
    2. 在派生类的用户节点,是不可转换的。

    要转换怎么办?(不推荐转换,见下面 Should I pointer-cast from a private derived class to its base class?

    需要强制类型转换

    1. p1 = (B1 *)p2;
    2. p1 = reinterpret_cast<B1 *>(p2);

     

    来自: https://isocpp.org/wiki/faq/private-inheritance

    • Inheritance — private and protected inheritance  

      Save to:

      InstapaperPocketReadability

      Contents of this section:

      How do you express “private inheritance”?  

      When you use : private instead of : public. E.g.,

       

      1. class Foo : private Bar {
      2. public:
      1. // ...
      1. };

      How are “private inheritance” and “composition” similar?  

      private inheritance is a syntactic variant of composition (AKA aggregation and/or has-a).

      E.g., the “Car has-a Engine” relationship can be expressed using simple composition:

       

      1. class Engine {
      2. public:
      1. Engine(int numCylinders);
      1. void start();                 // Starts this Engine
      1. };
      1. class Car {
      2. public:
      1. Car() : e_(8) { }             // Initializes this Car with 8 cylinders
      1. void start() { e_.start(); }  // Start this Car by starting its Engine
      2. private:
      1. Engine e_;                    // Car has-a Engine
      1. };

      The “Car has-a Engine” relationship can also be expressed using private inheritance:

       

      1. class Car : private Engine {    // Car has-a Engine
      2. public:
      1. Car() : Engine(8) { }         // Initializes this Car with 8 cylinders
      1. using Engine::start;          // Start this Car by starting its Engine
      1. };

      There are several similarities between these two variants:

      • In both cases there is exactly one Engine member object contained in every Car object
      • In neither case can users (outsiders) convert a Car* to an Engine*
      • In both cases the Car class has a start() method that calls the start() method on the contained Engine object.

      There are also several distinctions:

      • The simple-composition variant is needed if you want to contain several Engines per Car
      • The private-inheritance variant can introduce unnecessary multiple inheritance
      • The private-inheritance variant allows members of Car to convert a Car* to an Engine*
      • The private-inheritance variant allows access to the protected members of the base class
      • The private-inheritance variant allows Car to override Engine’s virtual functions
      • The private-inheritance variant makes it slightly simpler (20 characters compared to 28 characters) to give Car a start() method that simply calls through to the Engine’s start() method

      Note that private inheritance is usually used to gain access into the protected members of the base class, but this is usually a short-term solution (translation: a band-aid).

      Which should I prefer: composition or private inheritance?  

      Use composition when you can, private inheritance when you have to.

      Normally you don’t want to have access to the internals of too many other classes, and private inheritance gives you some of this extra power (and responsibility). But private inheritance isn’t evil; it’s just more expensive to maintain, since it increases the probability that someone will change something that will break your code.

      A legitimate, long-term use for private inheritance is when you want to build a class Fred that uses code in a class Wilma, and the code from class Wilma needs to invoke member functions from your new class, Fred. In this case, Fred calls non-virtuals in Wilma, and Wilma calls (usually pure virtuals) in itself, which are overridden by Fred. This would be much harder to do with composition.

       

      1. class Wilma {
      2. protected:
      3. void fredCallsWilma()
      1. {
      1. std::cout << "Wilma::fredCallsWilma()\n";
      2. wilmaCallsFred();
      1. }
      1. virtual void wilmaCallsFred() = 0;   // A pure virtual function
      1. };
      1. class Fred : private Wilma {
      2. public:
      3. void barney()
      1. {
      1. std::cout << "Fred::barney()\n";
      2. Wilma::fredCallsWilma();
      1. }
      1. protected:
      2. virtual void wilmaCallsFred()
      1. {
      1. std::cout << "Fred::wilmaCallsFred()\n";
      1. }
      2. };

      Should I pointer-cast from a private derived class to its base class?  

      Generally, No.

      From a member function or friend of a privately derived class, the relationship to the base class is known, and the upward conversion from PrivatelyDer* to Base* (or PrivatelyDer& to Base&) is safe; no cast is needed or recommended.

      However users of PrivatelyDer should avoid this unsafe conversion, since it is based on a private decision of PrivatelyDer, and is subject to change without notice.

      How is protected inheritance related to private inheritance?  

      Similarities: both allow overriding virtual functions in the private/protected base class, neither claims the derived is a kind-of its base.

      Dissimilarities: protected inheritance allows derived classes of derived classes to know about the inheritance relationship. Thus your grand kids are effectively exposed to your implementation details. This has both benefits (it allows derived classes of the protected derived class to exploit the relationship to the protected base class) and costs (the protected derived class can’t change the relationship without potentially breaking further derived classes).

      Protected inheritance uses the : protected syntax:

       

      1. class Car : protected Engine {
      2. public:
      1. // ...
      1. };

      What are the access rules with private and protected inheritance?  

      Take these classes as examples:

       

      1. class B                    { /*...*/ };
      2. class D_priv : private   B { /*...*/ };
      3. class D_prot : protected B { /*...*/ };
      4. class D_publ : public    B { /*...*/ };
      5. class UserClass            { B b; /*...*/ };

      None of the derived classes can access anything that is private in B. In D_priv, the public and protected parts of B are private. In D_prot, the public and protected parts of B are protected. In D_publ, the public parts of B are public and the protected parts of B are protected (D_publ is-a-kind-of-a B). class UserClass can access only the public parts of B, which “seals off” UserClass from B.

      To make a public member of B public in D_priv or D_prot, state the name of the member with a B:: prefix. E.g., to make member B::f(int,float) public in D_prot, you would say:

       

      1. class D_prot : protected B {
      2. public:
      3. using B::f;  // Note: Not using B::f(int,float)
      1. };

       

      来自 <https://isocpp.org/wiki/faq/private-inheritance>

       

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值