c++程序设计(第3版)第十一章笔记(上)

引言

通过对于c++第八章、第九章、第十章的初步学习我们可以里了解到面向对象程序设计的两个重要特征:数据抽象和封装,学会了在程序中使用类和对象,写出了基于对象的程序,这是面向对象程序设计的基础。

现在让我们复习一下面向对象程序设计的四个特点:抽象性,封装性,继承性,多态性。

那么接下来的学习我们即将接触面向对象程序设计当中的继承性。

面向对象技术强调软件的可重用性,极大的方便了程序的开发和人力物力资源的节省。那么继承性是如何做到这一点的呢?我们在具体的程序设计的过程当中,会发现有许多的程序模块拥有相同或者类似的功能,但我们在编写过程当中如果没有继承性的话还得重头再来写一个实现类似功能的程序模块, 很显然这样的工作量是极其巨大的。我们利用继承性可以不必重写功能相似的代码模块,只需要在其添加一些额外的功能与数据即可,这就是继承性的应用与作用。

继承与派生的概念

继承在c++当中是一个很重要的概念

例如在一个student类当中,有学号,名字,性别

class Student
{
	private:
		string name;
		int num;
		char sex;
	public:
		Student(string a,int b,char c){name=a,num=b,sex=c;}
		void display()
		{
			cout<<"name:"<<name<<endl;
			cout<<"num:"<<num<<endl;
			cout<<"sex:"<<sex<<endl;
		}
};

若在其中加上地址和年龄信息,则我们不利用继承的话还得重新创建一个类

class Student1
{
	private:
		string name;
		int num;
		char sex;
		int age;
		string address;
	public:
		Student(string a,int b,char c,int d,string e){name=a,num=b,sex=c,age=d,address=e;}
		void display()
		{
			cout<<"name:"<<name<<endl;
			cout<<"num:"<<num<<endl;
			cout<<"sex:"<<sex<<endl;
			cout<<"age:"<<age<<endl;
			cout<<"address"<<age<<endl; 
		}
};

很明显我们仅仅对于原Student类进行了些微的修改,明明有着许多的数据皆一致,却需要重新创建类,很显然是费时费力的。

那么我们可以利用继承性,使Student1类继承Student类的数据,从而减少工作量。c++的继承机制就是为了解决此类问题的出现。在c++当中所谓的继承就是在一个已存在的类的基础上建立一个新的类。已存在的类称为基类或者父类,新建立的类称为派生类或者子类。

一个新的类从已有的类哪里获得其已有属性,这种现象称为类的继承。通过继承,一个新建子类从已有的父类那里获得父类的特性。从另一个角度来说,从已有的父类产生一个新的子类叫做类的派生。类的继承是用已有的类来建立专用类的编程技术。

其实父类和子类的关系我们可以用数据结构当中的树来比较,把根节点比作祖宗也就是基类,把下面的叶节点比作儿子也就是派生类。不过派生类所拥有的基类不一定拥有,但是基类拥有的派生类一定会有。即拿人的一生来比喻,你小时候拥有的技能你长大后也会拥有,但是你长大后拥有的技能你小时候却不一定拥有,就是这个道理。

当一个派生类只从一个基类派生时,这被称作为单继承。一个派生类拥有两个及以上的基类称为多重继承。派生类是基类的具体化,

派生类是基类的具体化,基类是派生类的抽象。

派生类的声明方式

 模板

class 派生类名:[继承方式] 基类名
{
        派生类新增加的成员
}

在这里继承方式有多样:private,public,protected,若不声名则为private

例如我们把上例当中的Student1类声明为Student的派生类

class Student
{
	private:
		string name;
		int num;
		char sex;
	public:
		void display()
		{
			cout<<"name:"<<name<<endl;
			cout<<"num:"<<num<<endl;
			cout<<"sex:"<<sex<<endl;
		}
};
class Student1:public Student
{
	private:
		int age;
		string address;
	public:
		void display()
		{
			cout<<"age:"<<age<<endl;
			cout<<"address"<<age<<endl; 
		}
};

派生类的构成 

 派生类当中的成员包括从基类继承过来的成员和自己增加的成员两大部分。从基类继承的成员体现了派生类从基类继承而获得的共性,新增加的成员体现了派生类的个性。正是这些新增加的成员体现了派生类与基类的不同,体现了不同派生类之间的区别。

事实上并不是把基类的成员和派生类自己增加的成员简单地加载一起就成为派生类。构造一个派生类包括以下3部分工作:

1.从基类接收成员。

派生类对于基类的所有成员的要求的接收均为强制性的接收,派生类对于基类中的成员不可以选择性的接收,这也就导致了某些类在派生的过程之中有的数据不能够使用而占用内存空间,造成数据的传递过程当中也花费了不少的时间,降低了效率,这是目前c++无法解决的。所以在基类的设计当中我们也要用心,使派生过程中的冗余量最小。

2.调整从基类接受的成员。

虽然对于从基类接受的成员是无法选择的,但是程序员可以对于这些成员作出调整,例如改变成员的属性为private,或者说是直接替代。我们可以通过设置与基类当中同名的变量,则派生类当中的新成员会覆盖掉原有的变量。在进行函数的替代时,不仅仅要求函数名要相同,函数的形参表(参数的个数和类型)也要相同。如果不相同,则为函数的重载,而不是覆盖。

3.在声明派生类时增加的成员。

增加的成员也要经过精心设计,方便使用与理解。在声明派生类时,析构函数和构造函数是不能够从基类继承过来的,所以我们还应该手动定义。

从以上三点我们可以看出来:派生类是基类定义的延续,可以先声明一个基类,在此基类中只提供某些最基本的功能,而另外有些功能并未实现,然后在声明派生类时加入某些具体的功能,形成适用于某一特定应用的派生类。通过对基类声明的延续,将一个抽象的基类转化成具体的派生类。因此,派生类是抽象基类的具体实现

派生类成员的访问属性

对于基类成员和派生类自己增加的成员是按不同原则处理的,比如以下六种情况:

1.基类的成员函数访问基类成员

2.派生类的成员函数访问派生类自己增加的成员

3.基类的成员函数访问派生类的成员

4.派生类的成员函数访问基类的成员 

5.在派生类外访问派生类的成员

6.在派生类外访问基类的成员

对于1和2中的情况,经过我个人的实践发现,派生类仅仅可以访问基类中的public和protected成员,对于基类当中的private成员仍需要借助基类中public的对外接口访问。当然对于派生类自己增加的成员来说,可以通过本身的成员函数来访问。

对于3中的情况,基类成员函数只能访问基类成员,不可以访问派生类成员。

对于5中的情况,在派生类外可以访问派生类的公有成员,而不能够访问私有成员。

对于4和6当中的情况要具体讨论,比如继承关系

(1)公用继承

基类的公有成员和保护成员在派生类当中保持原有的访问属性,其私有成员仍为基类私有。

(2)私有继承

基类的公有成员和保护成员在派生类中成了私有成员。其私有成员仍为基类私有。

(3)受保护的继承

基类的公有成员和保护成员在派生类中成了私有成员,其私有成员仍为基类私有。保护成员的意思是,不能被外界引用,但可以被派生类的成员引用。

公有继承

即继承方式为public的继承称为公有继承,用公有继承方式建立的派生类称为公有派生类,其基类称为公有基类。在公有继承当中,基类的private数据对于派生类来说仍为private数据,是不可访问的成员,只有基类自己的成员函数能够引用。 但派生类能够访问基类当中的public和protected数据,且访问private数据的方式仅仅只有通过派生类函数调用基类函数,通过基类函数间接访问这一种方式可以实现。

 ​​​​​​​

c++系统的数据的封装性体现在基类的私有成员不可以在派生类当中被派生类的成员函数给直接引用,这样有利于测试、调试和修改系统。保护私有成员是一条重要的原则。

私有继承 

在声明一个派生类时将基类的继承方式指定为private称为私有继承,用私有方法建立的派生类称为私有派生类,其基类称为私有基类。 

私有基类的共用成员和保护乘员在派生类中的访问属性相当于派生类中的私有成员,及派生类的成员函数可以访问他们,而在派生类外不可以访问,私有基类的私有成员在派生类中称为不可访问成员,只有基类的成员函数可以引用它们。一个基类成员在基类当中的访问属性何在派生类当中的访问属性可能是不同的。私有基类的某些成员可以被基类的成员函数访问,但不能被派生类的成员函数访问。

即对于派生类来说,私有基类的公用成员和保护成员成为了派生类的私有成员,私有基类的私有成员被隐藏。

结论:

1.不能通过派生类对象引用从私有基类继承过来的任何成员

2.派生类的成员函数不能访问私有基类的私有成员,但是可以访问私有基类的公用成员受保护成员

虽然私有基类的私有成员不可以被派生类访问,不过派生类可以通过派生类的成员函数调用私有基类的公有成员函数,再利用私有基类的公有成员函数对于私有基类的私有数据进行访问。

保护成员和保护继承

 protected声明的成员为受保护的成员,简称保护成员

受保护的成员不可以被类外访问,但保护成员可以被派生类的成员函数引用。

如果基类声明了私有成员,那么任何的派生类都是不能访问他们的,若希望在派生类中能够访问他们,应当把他们声明为保护成员,如果在一个类当中声明了保护成员,那么也就意味着此类可能会用作基类。

在定义一个派生类时将基类的继承方式指定为protected的称为保护继承,用保护继承的方式建立的派生类称为保护派生类,其基类称为受保护的基类,简称保护基类。

保护基类的特点是:保护基类的公有成员和保护成员在派生类中都成了保护成员,其私有成员仍为基类私有,也就是把基类原有的公有成员也保护起来,不让类外随意访问。

1.保护基类的所有成员在派生类当中都被保护起来,类外不能访问,其公有成员和保护成员可以被派生类的成员函数访问私有成员则不可访问。

2.对于私有继承和保护继承比较来说,当仅仅有一个派生类时,作用相同。若有多个派生类时,私有基类的成员在新的派生类当都成为了不可访问的成员,无论在派生类内外都不可以访问。而保护继承中的成员在新的派生类当中为保护成员,可以被新生派生类的成员函数访问。

3.基类当中的私有成员被派生类继承后变为不可访问的成员,派生类当中的一切成员均无法访问他们,如果在派生类当中需要引用基类的某些成员,那么应当将基类的这些成员声明为protected而不是private。

4.在类的派生中,成员有四种不同的访问属性

(1)公有的,派生类内类外均可访问

(2)受保护的,派生类内可以访问,派生类外不可访问,下一层派生类可以访问

(3)私有的,派生类内可以放为,派生类外不可访问

(4) 不可访问的,派生类内类外均不可访问

(5)类的成员在不同作用域中有不同的访问属性

 多级派生时的访问属性

 

 类B是类A的直接派生类,类C是类A的间接派生类

在类的多级派生中,可以发现,把无论哪儿一种继承方式,在派生类是不能访问基类的私有成员的,私有成员只能被本类的成员函数所访问,毕竟派生类和基类不是同一个类。

如果在多级继承当中都采用公有继承,那么直到最后一级的派生类也能够访问基类的公用成员和保护成员。如果采用的是私有继承,经过若干次继承之后,基类的所有成员已经变成不可访问的了。如果采用保护继承,在派生类外是无法访问派生类中的任何成员的,而且经过多次派生之后,人们很难记清楚哪儿些成员可以访问哪儿些不可以访问,很容易出错。

因此在实际操作中,最常用的是公有继承。

猜你喜欢

转载自blog.csdn.net/couchpotatoshy/article/details/124559467
今日推荐