0
点赞
收藏
分享

微信扫一扫

继承与派生(Part Ⅰ)

继承与派生

  • 面向对象程序设计有4个主要特点:抽象、封装、继承和多态性
  • 面向对象计算强调软件的可重用性。C++语言提供了类的继承机制,解决了软件重用问题。

继承与派生的概念

  • 在C++中可重用性是通过"继承"这一机制来实现的。因此,继承是C++的一个重要组成部分。
  • 在C++中所谓"继承"就是在一个已存在的类的基础上建立一个新的类。已存在的类称为"基类(base class)“或"父类(father class)”。新建立的类称为"派生类(derived class)“或"子类(son class)”
  • 一个新类从已有的类那里获得其已有特性,这种现象称为类的继承。通过继承,一个新建子类从已有的父类那里获得父类的特性。从另一角度来说,从已有的类(父类)产生一个新的子类,称为类的派生
  • 派生类继承了基类的所有数据成员和成员函数(不包括基类的构造函数和析构函数),并可以增加自己的新成员,同时也可以调整来自基类的数据成员和成员函数。因此基类和派生类是相对而言的。一个基类可以派生出多个派生类,每一个派生类又可以作为基类再派生出新的派生类。一代一代地派生下去,就形成了类的继承层次结构
  • 一个派生类只从一个基类派生,这称为单继承(single inheritance),这种继承关系所形成的层次是一个树形结构。一个派生类不仅可以从一个基类派生,也可以从多个基类派生,也就是说,一个派生类可以有两个或多个基类(或者说,一个子类可以有两个或多个父类)。这种情况称为多重继承(multiple inheritance)。
  • 关于基类和派生类的关系,可以表述为:派生类是基类的具体化,而基类则是派生类的抽象

派生类的声明方式

  • 声明派生类的一般形式为:
class 派生类名:[继承方式] 基类名
{
	派生类新增加的成员
};
  • 继承方式包括:public(公用的),private(私有的),protected(受保护的),继承方式说可选项,如果不写此项,则默认为private(私有的)

例:假设已经声明了一个基类Student,在此基础上通过单继承建立一个派生类Student1

class Student1 :public Student //声明基类是Student
{
public:
	void display_1() //新增加的成员函数
	{
		cout << "age:" << age << endl;
		cout << "address:" << addr << endl;
	}
private:
	int age; //新增加的数据成员
	string addr; //新增加的数据成员
};

程序分析:

派生类的构成

  • 派生类的成员包括从基类继承过来的成员和自己增加的成员两大部分。从基类继承的成员体现了派生类从基类继承而获得的共性,而新增加的成员体现了派生类的个性。正是这些新增加的成员体现了派生类与基类的不同,也体现了不同派生类的区别 。
  • 实际上,并不是把基类的成员和派生类自己增加的成员简单的加在一起就成为派生类。构造一个派生类包括以下3个部分:
  1. 从基类接受成员
  1. 调整从基类接受的成员
  1. 在声明派生类时增加的成员
  • 通过以上的介绍可以看出:派生类是基类定义的延续。可以先声明一个基类,在此基类中只提供某些最基本的功能,而另外有些功能并未实现,然后在声明派生类时加入某些具体的功能,形成适用于某一特定应用的派生类。通过对基类声明的延续,将一个抽象的基类转化成具体的派生类。因此,派生类是抽象基类的具体实现

派生类成员的访问属性

  • 既然派生类中包含基类成员和派生类自己增加的成员,就产生了这两部分成员的关系和访问属性的问题。在建立派生类的时候,并不是简单地把基类的私有成员直接作为派生类的私有成员,把基类的公用成员直接作为派生类的公用成员。实际上,对基类成员和派生类自己增加的成员是按不同的原则处理的。
  • 具体说,在讨论访问属性时,要考虑以下几种情况:
  1. 基类的成员函数访问基类成员。
  2. 派生类的成员函数访问派生类自己增加的成员。
  3. 基类的成员函数访问派生类的成员。
  4. 派生类的成员函数访问基类的成员。
  5. 在派生类外访问派生类的成员。
  6. 在派生类外访问基类的成员。
  • 对于第(1)和第(2)种情况,比较简单,基类的成员函数可以访问基类成员,派生类的成员函数可以访问派生类成员。私有数据成员只能被同一类中的成员函数访问,公用成员可以被外界访问。
  • 第(3)种情况也比较明确,基类的成员函数只能访问基类的成员,而不能访问派生类的成员。
  • 第(5)种情况也比较明确,在派生类外可以访问派生类的公用成员,而不能访问派生类的私有成员。
  • 对于第(4)和第(6)种情况,就稍微复杂一些,也容易混淆。这牵涉到如何确定基类的成员在派生类中的访问属性的问题,不仅要考虑对基类成员所声明的访问属性,还要考虑派生类所声明的对基类的继承方式,根据这两个因素共同决定基类成员在派生类中的访问属性。

前面已提到,在派生类中,对基类的继承方式可以有public(公用的)、private (私有的)和protected(保护的)3种。不同的继承方式决定了基类成员在派生类中的访问属性。
简单地说:

  1. 公用继承(public inheritance)
    基类的公用成员和保护成员在派生类中保持原有访问属性,其私有成员仍为基类私有。
  2. 私有继承(private inheritance)
    基类的公用成员和保护成员在派生类中成了私有成员,其私有成员仍为基类私有。
  3. 受保护的继承(protected inheritance)
    基类的公用成员和保护成员在派生类中成了保护成员,其私有成员仍为基类私有。保护成员的意思是,不能被外界引用,但可以被派生类的成员引用。

公用继承

  • 在定义一个派生类时将基类的继承方式指定为public的,称为公用继承,用公用继承方式建立的派生类称为公用派生类(public derived class),其基类称为公用基类(public base class)。
  • 采用公用继承方式时,基类的公用成员和保护成员在派生类中仍然保持其公用成员和保护成员的属性,而基类的私有成员在派生类中并没有成为派生类的私有成员,它仍然是基类的私有成员,只有基类的成员函数可以引用它,而不能被派生类的成员函数引用,因此就成为派生类中的不可访问的成员

公用基类的成员在派生类中的访问属性:

在基类的访问属性继承方式在派生类中的访问属性
private(私有)public(公用)不可访问
public(公用)public(公用)public(公用)
protected(保护)public(公用)protected(保护)

例:访问公有基类的成员

#include<iostream>
using namespace std;
class Student //声明基类
{
public: //基类公用成员
   void get_value() //输入基类数据的成员函数
   {
      cin >> num >> name >> sex;
   }
   void display() //输出基类数据的成员函数
   {
      cout << "num: " << num << endl;
      cout << "name: " << name << endl;
      cout << "sex: " << sex << endl;
   }
private: //基类私有成员
   int num;
   string name;
   char sex;
};

class Student1 : public Student //以public方式声明派生类Student1
{
public:
    void get_value_1() //输入派生类数据
    {
        cin >> age >> addr;
    }
    void display_1()
    {
        cout << "num: " << num << endl; //试图引用基类的私有成员,错误
        cout << "name: " << name << endl; //试图引用基类的私有成员,错误
        cout << "sex: " << sex << endl; //试图引用基类的私有成员,错误
        cout << "age: " << age << endl; //引用派生类的私有成员,正确
        cout << "address: " << addr << endl; //引用派生类的私有成员,正确
    } 
private:
    int age;
    string addr;
};

程序分析:

可以将派生类Student1的声明改为:

class Student1 : public Student //以public方式声明派生类Student1
{
public:
    void get_value_1() //输入派生类数据
    {
        cin >> age >> addr;
    }
    void display_1()
    {
        cout << "age: " << age << endl; //引用派生类的私有成员,正确
        cout << "address: " << addr << endl; //引用派生类的私有成员,正确
    } 
private:
    int age;
    string addr;
};

然后在main函数中分别调用基类的display函数和派生类中的display_1函数,先后输出5个数据。
可以这样写main函数(假设对象stud中已有数据):

int main()
{
    Student1 stud; //定义派生类Student1的对象stud
    stud.get_value(); //调用基类的公有成员函数,输入基类中3个数据成员的值
    stud.get_value_1(); //调用派生类公有成员函数,输出派生类中2个数据成员的值
    stud.display(); //调用基类的公用成员函数,输出基类中3个数据成员的值
    stud.display_1(); //调用派生类公有成员函数,输出派生类中2个数据成员的值
    return 0;
}

运行结果:

stud.age = 18; //错误,在类外不能引用派生类的私有成员
stud.num = 10020; //错误,在类外不能引用基类的私有成员

私有继承

  • 在声明一个派生类时将基类的继承方式指定为private的,称为私有继承,用私有继承方式建立的派生类称为私有派生类(private derived class), 其基类称为私有基类(private base class)。
  • 私有基类的公用成员和保护成员在派生类中的访问属性相当于派生类中的私有成员,即派生类的成员函数能访问它们,而在派生类外不能访问它们。私有基类的私有成员在派生类中成为不可访问的成员,只有基类的成员函数可以引用它们。一个基类成员在基类中的访问属性和在派生类中的访问属性可能是不同的。私有基类的成员可以被基类的成员函数访问,但不能被派生类的成员函数访问。

私有基类在派生类中的访问属性:

在基类的访问属性继承方式在派生类中的访问属性
private(私有)private(私有)不可访问
public(公用)private(私有)private(私有)
protected(保护)private(私有)private(私有)

若基类A有公用数据成员i和j,私有数据成员k,采用私有继承方式声明了派生类B,新增加了公用数据成员m和n,私有数据成员p。在派生类B作用域内,基类A的公用数据成员i和j呈现私有成员的特征,在派生类B内可以访问它们,而在派生类B外不可访问它们。在派生类内不可访问基类A的私有数据成员k。此时,从派生类的角度来看,相当于有公用数据成员m和n,私有成员i,j,p。基类A的私有数据员k在派生类B中成为"不可见"的。

  • 表格中的规定不必死记,只须理解:既然声明为私有继承,就表示将原来能被外界引用的成员隐藏起来,不让外界引用,因此私有基类的公用成员和保护成员理所当然地成为派生类中的私有成员。私有基类的私有成员按规定只能被基类的成员函数引用,在基类外当然不能访问它们,因此它们在派生类中是隐蔽的不可访问的。
  • 对于不需要再往下继承的类的功能可以用私有继承方式把它隐蔽起来,这样,下一层的派生类无法访问它的任何成员。
  • 可以知道:一个成员在不同的派生层次中的访问属性可能是不同的。它与继承式有关。

例:将上例中地公用继承方式改为用私有继承方式(基类Student不改)

class Student1 : private Student //用私有继承方式声明派生类Student1
{
public:
    void get_value_1() //输入派生类数据
    {
        cin >> num >> name >> sex;
    }
    void display_1()
    {
        cout << "age: " << age << endl; //引用派生类的私有成员,正确
        cout << "address: " << addr << endl; //引用派生类的私有成员,正确
        display();
    } 
private:
    int age;
    string addr;
};

int main()
{
    Student1 stud; //定义派生类Student1的对象stud
    stud.display(); //错误,私有基类的公用成员函数在派生类中是私有的
    stud.display_1(); //正确,display_1函数是Student1类的公用函数
    stud.age = 18; //错误,外界不能引用派生类的私有成员
    return 0;
}

结论:

  1. 不能通过派生类对象引用从私有基类继承过来的任何成员
  2. 派生类的成员函数不能访问私有基类的私有成员,但可以访问私有基类的公用成员
  • 虽然在派生类外不能通过派生类对象调用私有基类的公用成员函数,但可以通过派生类的成员函数调用私有基类的公用成员函数(此时它是派生类中的私有成员函数,可以被派生类的任何成员函数调用)。

可将上面的私有派生类的两个成员函数定义改写为:

void get_value_1() //输入5个数据的函数
{
	get_value(); //调用基类的公用函数,输入基类3个数据
	cin >> age >> addr; //输入派生类2个数据
}
void display_1() //输出5个数据成员的值
{
	cout << "age: " << age << endl; //输出派生类的私有数据成员
	cout << "address: " << addr << endl; //输出派生类的私有数据成员
}
int main()
{
    Student1 stud1; //定义派生类Student1的对象stud
    stud1.get_value_1(); //get_value_1是派生类Student1类的公用函数
    stud1.display_1();  //display_1是派生类Student1类的公用函数
    return 0;
} 
举报

相关推荐

0 条评论