解析C++编程中的继承方面的运用

C++继承与组合详解
我们知道,在一个类中可以用类对象作为数据成员,即子对象(详情请查看:C++有子对象的派生类的构造函数)。实际上,对象成员的类型可以是本派生类的基类,也可以是另外一个已定义的类。在一个类中以另一个类的对象作为数据成员的,称为类的组合(composition)。

例如,声明Professor(教授)类是Teacher(教师)类的派生类,另有一个类BirthDate(生日),包含year,month,day等数据成员。可以将教授生日的信息加入到Professor类的声明中。如:

class Teacher //教师类
{
public:
  // Some Code
private:
  int num;
  string name;
  char sex;
};
class BirthDate //生日类
{
public:
  // Some Code
private:
  int year;
  int month;
  int day;
};
class Professor:public Teacher //教授类
{
public:
  // Some Code
private:
  BirthDate birthday; //BirthDate类的对象作为数据成员
};

类的组合和继承一样,是软件重用的重要方式。组合和继承都是有效地利用已有类的资源。但二者的概念和用法不同。通过继承建立了派生类与基类的关系,它是一种 “是”的关系,如“白猫是猫”,“黑人是人”,派生类是基类的具体化实现,是基类中的一 种。通过组合建立了成员类与组合类(或称复合类)的关系,在本例中BirthDate是成员类,Professor是组合类(在一个类中又包含另一个类的对象成员)。它们之间不是‘‘是”的 关系,而是“有”的关系。不能说教授(Professor)是一个生日(BirthDate),只能说教授(Professor)有一个生日(BirthDate)的属性。

Professor类通过继承,从Teacher类得到了num,name,age,sex等数据成员,通过组合,从BirthDate类得到了year,month,day等数据成员。继承是纵向的,组合是横向的。

如果定义了Professor对象prof1,显然prof1包含了生日的信息。通过这种方法有效地组织和利用现有的类,大大减少了工作量。如果有

  void fun1(Teacher &);
  void fun2(BirthDate &);

在main函数中调用这两个函数:

 fun1(prof1); //正确,形参为Teacher类对象的引用,实参为Teacher类的子类对象,与之赋值兼容
  fun2(prof1.birthday); //正确,实参与形参类型相同,都是BirthDate类对象
  fun2(prof1); //错误,形参要求是BirthDate类对象,而prof1是Professor类型,不匹配

如果修改了成员类的部分内容,只要成员类的公用接口(如头文件名)不变,如无必要,组合类可以不修改。但组合类需要重新编译。

继承在软件开发中的重要意义
继承是面向对象技术的重要内容,有了继承,使软件的重用成为可能。

过去,软件人员开发新的软件,能从已有的软件中直接选用完全符合要求的部件不 多,一般都要进行许多修改才能使用,实际上有相当部分要重新编写,工作童很大。缩短软件开发过程的关键是鼓励软件重用。继承机制解决了这个问題。编写面向对象的程序时要把注意力放在实现对自己有用的类上面,对已有的类加以整理和分类,进行剪裁和修改,在此基础上集中精力编写派生类新增加的部分,使这些类能够被程序设计的许多领域使用。继承是C++和C的蟑重要的区别之一。

由于C++提供了继承的机制,这就吸引了许多厂商开发各类实用的类库。用户将它们作为基类去建立适合于自己的类(即派生类),并在此基础上设计自己的应用程序。类库的出现使得软件的重用更加方便,现在有一些类库是随着C++编译系统卖给用户的。读者不要认为类库是C++编译系统的一部分。不同的C++编译系统提供的由不同厂商开发的类库一般是不同的。在一个C++编译系统环境下利用类库开发的稈序,在另一种C++编译系统环境下可能不能工作,除非把类库也移植过去。考虑到广大用户的情况,目前随C++编译系统提供的类库是比较通用的,但它的针对性和实用范围也随之受到限制。 随着C ++在全球的迅速推广,在世界范围内开发用于各个领域的类库的工作正日益兴旺。

对类库中类的声明一般放在头文件中,类的实现(函数的定义部分)是单独编译的,以目标代码形式存放在系统某一目录下。用户使用类库时,不需要了解源代码,但必须知道头文件的使用方法和怎样去连接这些目标代码(在哪个子目录下),以便源程序在编译后与之连接。

由于基类是单独编译的,在程序编译时只需对派生类新增的功能进行编译,这就大大提高了调试程序的效率。如果在必要时修改了基类,只要基类的公用接口不变,派生类不必修改,但基类需要重新编译,派生类也必须重新编译,否则不起作用。

那么,人们为什么这么看重继承,要求在软件开发中使用继承机制,尽可能地通过继承建立一批新的类?为什么不是将已有的类加以修改,使之满足自己应用的要求呢?

归纳起来,有以下几个原因:
有许多基类是被程序的其他部分或其他程序使用的,这些程序要求保留原有的 基类不受破坏。使用继承是建立新的数据类型,它继承了基类的所有特征,但不改变基类本身。基类的名称、构成和访问属性丝毫没有改变,不会影响其他程序的使用。
用户往往得不到基类的源代码。如果想修改已有的类,必须掌握类的声明和类的实现(成员函数的定义)的源代码。但是,如果使用类库,用户是无法知道成员函数的代码的,因此也就无法对基类进行修改。
在类库中,一个基类可能已被指定与用户所需的多种组件建立了某种关系,因此 在类库中的基类是不容许修改的(即使用户知道了源代码,也决不允许修改)。
实际上,许多基类并不是从已有的其他程序中选取来的,而是专门作为基类设计的。有些基类可能并没有什么独立的功能,只是一个框架,或者说是抽象类。人们根据需要设计了一批能适用于不同用途的通用类,目的是建立通用的数据结构,以便用户在此基础上添加各种功能,从而建立各种功能的派生类。
在面向对象程序设计中,需要设计类的层次结构,从最初的抽象类出发,每一层派生类的建立都逐步地向着目标的具体实现前进,换句话说,是不断地从抽象到具体的过 程。每一层的派生和继承都需要站在整个系统的角度统一规划,精心组织。

(0)

相关推荐

  • C++模板二段名字查找方法

    如下所示: #include<iostream> using namespace std; void f(){ cout<<"global f()"<<endl; } template<typename T> class A { public: void f() { cout << "A::f()" << endl; } }; template<typename T> class B

  • 浅谈C++继承中的名字查找

    实例如下: #include<iostream> #include<string> using namespace std; class Base { public: void func() { cout << "func() in Base." << endl; } void func(int a) { cout << "func(int a) in Base." << endl; } voi

  • C++继承中的访问控制实例分析

    本文较为深入的探讨了C++继承中的访问控制,对深入掌握C++面向对象程序设计是非常必要的.具体内容如下: 通常来说,我们认为一个类有两种不同的用户:普通用户 和 类的实现者.其中,普通用户编写的代码使用类的对象,这部分代码只能访问类的公有(接口)成员:实现者则负责编写类的成员和友元的代码,成员和友元既能访问类的公有部分,也能访问类的私有部分.如果进一步考虑继承的话就会出现第三种用户,即派生类.派生类可以访问基类的公有(public)成员和受保护(protected)成员,但不能访问基类的私有(p

  • 解析C++编程中的继承方面的运用

    C++继承与组合详解 我们知道,在一个类中可以用类对象作为数据成员,即子对象(详情请查看:C++有子对象的派生类的构造函数).实际上,对象成员的类型可以是本派生类的基类,也可以是另外一个已定义的类.在一个类中以另一个类的对象作为数据成员的,称为类的组合(composition). 例如,声明Professor(教授)类是Teacher(教师)类的派生类,另有一个类BirthDate(生日),包含year,month,day等数据成员.可以将教授生日的信息加入到Professor类的声明中.如:

  • 深入解析Swift编程中的构造方法

    一.引言 构造方法是一个类创建对象最先也是必须调用的方法,在Objective-C中,开发者更习惯称这类方法为初始化方法.在Objective-C中的初始化方法与普通函数相比除了要以init抬头外并无太严格的分界,而在Swift语言体系中,构造方法与普通的方法分界十分严格,从格式写法上就有不同,普通方法函数要以func声明,构造方法统一为init命名,不需要func关键字声明,不同的构造方法采用方法重载的方式创建. 二.构造方法的复写与重载 在Objective-C中,不同的初始化方法就是不同的

  • 深入解析C++编程中基类与基类的继承的相关知识

    基类 继承过程将创建一个新的派生类,它由基类的成员加上派生类添加的任何新成员组成.在多重继承中,可以构建一个继承关系图,其中相同的基类是多个派生类的一部分.下图显示了此类关系图. 单个基类的多个实例 在该图中,显示了 CollectibleString 和 CollectibleSortable 的组件的图形化表示形式.但是,基类 Collectible 位于通过 CollectibleSortableString 路径和 CollectibleString 路径的 CollectibleSor

  • 解析C++编程中virtual声明的虚函数以及单个继承

    虚函数 虚函数是应在派生类中重新定义的成员函数. 当使用指针或对基类的引用来引用派生的类对象时,可以为该对象调用虚函数并执行该函数的派生类版本. 虚函数确保为该对象调用正确的函数,这与用于进行函数调用的表达式无关. 假定基类包含声明为 virtual 的函数,并且派生类定义了相同的函数. 为派生类的对象调用派生类中的函数,即使它是使用指针或对基类的引用来调用的. 以下示例显示了一个基类,它提供了 PrintBalance 函数和两个派生类的实现 // deriv_VirtualFunctions

  • 解析Swift语言面相对象编程中的继承特性

    取大于形态的能力被定义为继承.一般一个类可以从另一个类继承属性和方法.类可以进一步划分到子类和超类. 子类:当一个类从另一个类继承属性,方法和功能被称为子类 超类:类包含属性,方法和功能被其它类继承称为超类 Swift 中类包含父类和调用访问方法,属性,功能和重写方法.另外,属性观察者也用于添加属性和修改所存储的或计算的特性的方法. 基类 一个类如果不从其它类继承方法,属性或功能,那么它被称为"基类". 复制代码 代码如下: classStudDetails{var stname:St

  • 深入解析Java编程中final关键字的作用

    final class 当一个类被定义成final class,表示该类的不能被其他类继承,即不能用在extends之后.否则在编译期间就会得到错误. package com.iderzheng.finalkeyword; public final class FinalClass { } // Error: cannot inherit from final class PackageClass extends FinalClass { } Java支持把class定义成final,似乎违背了

  • 深入解析C++编程中的纯虚函数和抽象类

    C++纯虚函数详解 有时在基类中将某一成员函数定为虚函数,并不是基类本身的要求,而是考虑到派生类的需要,在基类中预留了一个函数名,具体功能留给派生类根据需要去定义. 纯虚函数是在声明虚函数时被"初始化"为0的函数.声明纯虚函数的一般形式是 virtual 函数类型 函数名 (参数表列) = 0; 关于纯虚函数需要注意的几点: 纯虚函数没有函数体: 最后面的"=0"并不表示函数返回值为0,它只起形式上的作用,告诉编译系统"这是纯虚函数"; 这是一个

  • 详解C#面相对象编程中的继承特性

    继承(加上封装和多态性)是面向对象的编程的三个主要特性(也称为"支柱")之一. 继承用于创建可重用.扩展和修改在其他类中定义的行为的新类.其成员被继承的类称为"基类",继承这些成员的类称为"派生类".派生类只能有一个直接基类.但是,继承是可传递的.如果 ClassB 派生出 ClassC,ClassA 派生出 ClassB,则 ClassC 会继承 ClassB 和 ClassA 中声明的成员. 注意 结构不支持继承,但可以实现接口. 从概念上来

  • 深入解析Java编程中方法的参数传递

    在阅读本文之前,根据自己的经验和理解,大家可以先思考并选择一下Java函数的参数传递方式: A. 是按值传递的? B. 按引用传递的? C. 部分按值部分按引用? 此处暂不宣布正确答案,我们通过一个简单的例子让大家自己找答案: 1. 先定义一个类型Value public static class Value { private String value = "value"; public String getValue() { return value; } public void

  • 解析Python编程中的包结构

    假设你想设计一个模块集(也就是一个"包")来统一处理声音文件和声音数据.通常由它们的扩展有不同的声音格式,例如:WAV,AIFF,AU),所以你可能需要创建和维护一个不断增长的各种文件格式之间的转换的模块集合.并且可能要执行声音数据处理(如混合,添加回声,应用平衡功能),所以你写一个永无止境的流模块来执行这些操作:模块设计的包如下: sound/ Top-level package __init__.py Initialize the sound package formats/ Su

随机推荐