会员: 密码:  免费注册 | 忘记密码 | 会员登录 网页功能: 加入收藏 设为首页 网站搜索  
游戏开发 > 程序设计 > 设计思想
C++基本功和 Design Pattern系列(6) public,protect,private inheritance
发表日期:2007-03-28 19:09:26作者: 出处:  


C++基本功和 Design Pattern系列(6) public,protect,private inheritance


======================================================
 大家请把我的文章当参考,详细内容  还请参照 权威书籍 
 <c++ programming language>如果文中有错误和遗漏,
 请指出,Aear会尽力更正, 谢谢!
Aear Blog: http://blog.sina.com.cn/u/1261532101
======================================================

今天讲的是 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.

今天就说这么多,有空来我的Blog做客:http://blog.sina.com.cn/u/1261532101 ,下次见!

返回顶部】 【打印本页】 【关闭窗口

关于我们 / 给我留言 / 版权举报 / 意见建议 / 网站编程QQ群   
Copyright ©2003- 2024 Lihuasoft.net webmaster(at)lihuasoft.net 加载时间 0.00276