Public Protect Private 常识(一)

今天讲的是 public inheritance, protected inheritance & private inheritance,内容不多,但是非常重要。基本的类的继承,也就是inheritance的概念大家都清楚,明确的定义不再详细说明了。先面举个例子来说明:

class People {
    ...
    Walk();
    Eat();
};

class Student : public People{
   ...
   Study();
};

注意这行:
    class Student : public People {
中的public,表明是public inheritance,如果换成protected,就是protected inheritance, private就是private inhertance. 首先需要说明的是3种inheritance在语法上相似,但是在语意上完全不同。我们先从public inheritance说起。

=====================public inheritance=====================

public inheritance最基本的概念就是"isa" ( is a )。简单的说,继承类也就是Derived Class "is a" Base Class. 用上面的例子来说,People是base class, Student是 derived class,所以能够推导出: “student is a people” 这句话。如果你无法推导出 "isa"的关系,那么就不应该使用public inheritance.

其次,即使是能推导出 "isa" 的关系,也必须满足2个条件,才能使用 public inheritance. 这2个条件是:

    1. 所有Base Class的属性,也就是 attribute,Derived Class都有。
    2. 所有Base Class的方法,Derived Class都应该包含。

在上面的例子中,student也是个people,所以能够Walk() 和 Eat(),因此public inheritance 是合理的。
如果满足 "isa" 但是不满足上述条件,建议使用 Delegation/Composition,具体关于Delegation和Composition,在"C++基本功和 Design Pattern系列(1)" 中有说明。让我们看下在《Effective C++》中的一个例子来说明这种情况:

class Rectangle {
    ...
    SetWidth();
    SetHeight();
};

class Square : public Rectangle {
    ...
    SetLength();
};

我们大家都知道,一个正方形Square,一定是一个长方形Rectangle,所以满足"isa"的条件。我们给Rectangle提供了SetWidth()和SetHeight()的方法。如果不考虑上面2条,只考虑 "isa",那么这个 public inheritance是合理的,但是让我们看看会出现什么问题。

在Square中我们要求长和宽必须相等,因此我们提供了SetLength(),来同时设置正方形的长和宽。但是有一位Bill小朋友无法分辨长方形和正方形,因此写出了如下代码:

    Square MySquare;
    MySquare.SetWidth(100);
    MySquare.SetLenght(200);

那么问题出现了,MySquare并不是一个Square。相信大家都明白了吧。语言的不精确性导致在设计过程中出现的错误是屡见不鲜的。因此,在 public inheritance的时候要特别注意。也许有人会说,我们把SetHeight 和 SetWidth设置成Virtual然后在Square Class中重载不就可以了吗?如果Rectangle和Square 2个class都是你来写,那么也许不会出现问题。但是如果一个非常复杂的class,包含几十个方法和几十个属性,并且由别人来写,那么你会不会仔细的阅读代码并且overlord每一个需要的方法呢?即使你这样做了,也许会带来更多的麻烦。因为有可能破坏内部数据的一致性。

让我们来看看interface inheritance的例子:

    Class Bird {
       ...
       virtual Fly() = 0;
    };

    Class Turkey : public Bird {
       ...
       Fly() { cout << "I cannt fly! Jessus....." <<endl; };
    };

    Turkey Bird0;
    ...
    Bird0.Flg();   // runtime error

首先,鸟能飞,这个没有问题,火鸡是一种鸟,这也没有问题,但是:火鸡不能飞。问题出现了,client能够调用Turkey的Fly()方法,但是得到的确是一个 RunTime Error! 这里必须强调下:"RUNTIME ERROR!",对于游戏程序来说,一个"RUNTIME ERROR"基本上就等于程序崩溃。和out of memory同等性质。如果你玩WOW做7个小时中间不能间断的任务,然后出现一只火鸡给个RUNTIME ERROR....我想是人都会崩溃吧。

所以对于这种错误,我们要在编译的时候尽量查出来,也就是 Prefer Compile Error over Runtime Error. 通过更改类的设计,我们可以避免类似的runtime error:
    Class Bird {
       ...
    };
  
    Class UnflyableBird : public Bird{
       ...
       // no fly() here
    };

    Class Turkey : public UnflyableBird {
       ...
    };

    Turkey Bird0;
    ...
    Bird0.Flg();   // compile error....
  
所以,要想使用public inheritance,必须满足:

    1. "ISA"
    2. 所有Base Class的属性,也就是 attribute,Derived Class都有。
    3. 所有Base Class的方法,Derived Class都应该包含。


=====================private inheritance=====================

private inheritance和public inheritance最大的区别就在于,private inheritance不满足"isa"的关系。举个例子:

class People {
    ...
    Walk();
    Eat();
};

class ET: private People{
   ...
};

外星人ET是一种类似人的生物,能做一些类似人的动作,但是并不是人。从C++的语法上面来讲,下面的代码是错误的:

    People*  p = new ET();   // ERROR, ET is not a People

使用private inheritance的目的只是简单的为了代码重用。因此如果不满足public inheritance的条件,可以使用 Delegation/composition 和 Private Inheritance。 那么在什么情况下使用 private inheritance,什么情况下使用Delegation/Composition 呢?

有2种情况是推荐使用 private inheritance的,其他的情况下,推荐使用Delegation/Composition.

情况1: 需要对Base Class中的 private/protect virtual 进行重载。比如类似Draw() 等等。

情况2: 不希望一个Base class被 client使用。

关于情况2,举个简单的例子:
如果我们不希望Base Class被别人直接使用,有2种方法,第一是:把它设置成为abstract class, 也就是包含pure virtual function. 第2种方法是把constructor 和 descturctor设置成 protected.代码如下:

class Base {
protected:
   Base(); 
   virtual ~Base();
};

class Derived : private Base {
    ...
};

Base n; // Error, Base() cannot be called
Derived m; // ok, Derived can call Base()

这样我们又可以保证n的代码可以被m使用,又可以防止 client直接调用 Base进行我们不希望的操作。

=====================protected inheritance=====================

protected inheritance和 private inheritance没有本质的区别,但是如果我们希望的 Derived Class 能够作为其他 class的基类,那么就应该使用 protected inheritance. 

引自原来在CGD讨论的几点总结:
1. public继承被称为类型继承(type inheritance)。往往反映is-a关系。
2. protected继承基类的所有公有成员都成为派生类的protected成员。这意味着它们可以被后来从该类派生的类访问,但不能在层次结构之外被访问。
3. private继承被称为实现继承(implementation inheritance)。派生类提供自己的公有接口,重用基类的实现。 

 

数据成员的访问域变化:
1. public继承的话,原来父类的public ,protected成员继承到子类中,类型不变员
2. protected继承的话,原来父类的public ,protected成员继承到子类中,并成为protected类型成员
3. private继承的话,原来父类的public ,protected成员继承到子类中,并成为private类型成员

PS一下
理想情况下,是能不用public就不用,因为一旦public就引发一系列如client可以用base pointer去new inherit class,则dtor需要是virtual的问题
但大多数情况下都是public的情况,大家对此也比较熟悉,所以下面谈谈不常见的情况

在private inheritance时,如果不存在必须实现virtual method的情况下,推荐选择composition,避免了未来可能的multi inheritance导致出现的讨厌的菱形结构从而引发出现的C++阴暗面: virtual inheritance,即不要因为只是想少敲点代码而失去了良好的设计

protected和private区别在于:如果只是为了重用code,在不能composition的情况下则优先考虑private,除非派生类还会需要或是有可能会被其他类继承的话,才需要变成protected。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值