学习笔记之深入浅出MFC 第8章 C++重要性质----基类与派生类:谈继承(Inheritance)

来源:互联网 发布:绝食 水 知乎 编辑:程序博客网 时间:2024/04/29 18:31

继承是C++最神秘而特有的性质。矩形是形,椭圆形是形,三角形也是形。苍蝇是昆虫,蜜蜂是昆虫,蚂蚁也是昆虫。人们习惯把相同的性质抽离出来,成立一个基类(base class),再从中衍化出派生类(derived class) 。所以,以形状为例,我们可以得到下面这样的类层次,从而进一步说明继承的原理:

我们接下来用程序来表现一下上面形状的继承性:

首先,定义基类形状,所有的类从这个类继承

class  CShape;         //形状

private:

int    m_color;

public:

void setcolor( int  color)     {  m_color = color;   }

};

在基类中定义的是从它分出去的所有子类的共有属性,比如这里的颜色m_color,设置颜色setcolor,每一个子类都有这两个属性,而且都是同一个函数可以实现的。

接下来是第一级继承,从形状继承出矩形、椭圆形和三角形,因为这三个都是形状:

class  CRect  :   public   CShape       //矩形是一种形状

{//它会继承m_color和setcolor()

public:

void display()  {   ...   }

};


class  CEllipse  :   public   CShape       //椭圆形是一种形状

{//它会继承m_color和setcolor()

public:

void display()  {   ...   }

};

class  CTriangle  :   public   CShape       //三角形是一种形状

{//它会继承m_color和setcolor()

public:

void display()  {   ...   }

};

上面三种图形都是继承自形状基类,所以都具有m_color和setcolor()功能,这时所有形状的共性。但是,针对每一种形状,其绘图函数display()都是不一样的,所以display()是它们各自的特性,需要单独在每一个子类中声明。

那么,接下来子类还可以往下继承,比如正方形也是一种矩形,圆形也是一种椭圆形

class  CSquare  :   public   CRect       //正方形是一种矩形

{

public:

void display()  {   ...   }

};

class  CCircle :   public   CEllipse       //圆形是一种椭圆形

{

public:

void display()  {   ...   }

};


于是,可以这么操作:

CSquare square;

CRect rect1, rect2;

CCircle circle;


square.setcolor(1); //令square.m_color = 1;

square.display(); //调用CSquare::disolay


rect1.setcolor(2); //令rect1.m_color = 2;

rect1.display(); //调用CRect::disolay


rect2.setcolor(3); //令rect2.m_color = 3;

rect2.display(); //调用CRect::disolay


circle.setcolor(4); //令circle.m_color = 4;

circle.display(); //调用CCircle::disolay


特别需要注意的是第一个和第四个,父类中有display()子类中也有display(),那么到底调用的是哪一个呢?上面注释中说明的很清楚,子类中有的调用的是子类的,子类中没有的调用的是父类的。这是多态性,后面我们会讲到的。

需要注意以下这些事实与问题:

(1)、所有的类都由CShape派生下来,所以它们都自然而然继承了CShape的成员,包括变量和函数。也就是说,所有的形状都“暗自”具备了m_color变量和setcolor函数。所谓的“暗自”,意思就是无法从各派生类的声明中直接看出来。

(2)、两个矩形对象rect1和rect2各有自己的m_color,但是关于setcolor函数却是共享相同的CRect::setcolor(其实更应该说是CShape::setcolor)。下面图表表示期间关系:

同一个函数如何处理不同的数据?为什么rect1.setcolor和rect2.setcolor明明都是调用CRect::setcolor(其实也就是CShape::setcolor),却能有条不紊地分别处理rect1.m_color和rect2.m_color?答案在于this指针。下一节看完this指针我们再回过头来看看这里是为什么。

(3)、display之所以不提升到基类中,是因为每一个子类的形状不同,所以每一个的display()其实是不一样的,如果是用继承的方法获得的display()每个子类都是完全一样的。

this指针

还记得上面第二点的困惑吗?两个矩阵对象rect1和rect2各有自己的m_color成员变量,但是rect1.setcolor和rect2.setcolor却都是调用的同一个成员函数CRect::setcolor,我们就困惑了,CRect::setcolor怎么处理不同对象的m_color呢?答案就是:成员函数有一个隐藏参数,名称为this指针。

当你调用:rect1.setcolor(2);     //rect1是CRect对象

rect2.setcolor(3);     //rect2是CRect对象 

时,编译器实际上为你做出来的编码是:

CRect::setcolor(2, (CRect*)&rect1 );

CRect::setcolor(3, (CRect*)&rect2 );

多出来的参数就是所谓的this指针。也就是说,我们在调用继承自父类的函数的时候,调用的函数仍旧是父类的函数,但是在函数中会加上这是哪一个子类在调用的参数,这个参数就标明了当前调用的子类。

其实在每一个类定义中,都有一个这样被省略的指针:

class CShape

{

public:

void setcolor(int color) { m_color = color; }

};

被编译之后其实是这样的:

class CShape

{

public:

void setcolor(int color , (CShape*)this) { this-> m_color = color; }

};

0 0