C++单继承

在《基类和派生类》中讲述了单继承的基本概念,这节着重讲述继承的具体应用。

  在单继承中,每个类可以有多个派生类,但是每个派生类只能有一个基类,从而形成树形结构。

  成员访问权限的控制

  在《基类和派生类》一讲中,我们讲述了派生类和派生类的对象对基类成员的访问权限的若干规定,这里通过一个实例进一步讨论访问权限的具体控制,然后得出在使用三种继承方式时的调用方法。

//继承性的public继承方式的访问权限的例子
#include

file://定义基类A
class A
{
public:
A() { cout<<"类A的构造函数!"< A(int a) { Aa = a, aa = a, aaa = a; }
void Aprint() { cout<<"类A打印自己的private成员aa:"< int Aa;
private:
int aa;
protected:
int aaa;
};

file://定义由基类A派生的类B
class B : public A
{
public:
B() { cout<<"类B的构造函数!"< B(int i, int j, int k);
void Bprint() { cout<<"类B打印自己的private成员bb和protected成员bbb:"< void B_Aprint() { cout<<"类B的public函数访问类A的public数据成员Aa:"< cout<<"类B的public函数访问类A的protected数据成员aaa:"< GetAaaa();
GetAaaa1();}
private:
int bb;
void GetAaaa() { cout<<"类B的private函数访问类A的public数据成员Aa:"< cout<<"类B的private函数访问类A的protected数据成员aaa:"< protected:
int bbb;
void GetAaaa1() { cout<<"类B的protected函数访问类A的public数据成员Aa:"< cout<<"类B的protected函数访问类A的protected数据成员aaa:"< };

file://基类B的构造函数,需负责对基类A的构造函数的初始化
B::B(int i, int j, int k):A(i), bb(j), bbb(k) {}

file://程序主函数
void main()
{
B b1(100, 200, 300); file://定义类B的一个对象b1,并初始化构造函数和基类构造函数
b1.B_Aprint(); file://类B调用自己的成员函数B_Aprint函数
b1.Bprint(); file://类B对象b1访问自己的private和protected成员
b1.Aprint(); file://通过类B的对象b1调用类A的public成员函数
}


  该程序的输出结果为:

   类B的public函数访问类A的public数据成员Aa:100

   类B的public函数访问类A的protected数据成员aaa:100

   类B的private函数访问类A的public数据成员Aa:100

   类B的private函数访问类A的protected数据成员aaa:100

   类B的protected函数访问类A的public数据成员Aa:100

   类B的protected函数访问类A的protected数据成员aaa:100

   类B打印自己的private成员bb和protected成员bbb:200,300

   类A打印自己的private成员aa:100

  上述是属public继承方式,我们可以得出以下结论:

  在公有继承(public)时,派生类的public、private、protected型的成员函数可以访问基类中的公有成员和保护成员;派生类的对象仅可访问基类中的公有成员。

  让我们把继承方式public改为private,编译结果出现1处如下错误:

   'Aprint' : cannot access public member declared in class 'A'

  出错语句在于:b1.Aprint();,因此,我们可以得出以下结论:

  在公有继承(private)时,派生类的public、private、protected型的成员函数可以访问基类中的公有成员和保护成员;但派生类的对象不可访问基类中的任何成员。另,使用class关键字定义类时,缺省的继承方式是private,也就是说,当继承方式为私有继承时,可以省略private。

  让我们把继承方式public改为protected,可以看出,结果和private继承方式一样。

  构造函数和析构函数

  派生类的构造函数和析构函数的构造是讨论的主要问题,读者要掌握它。

  1. 构造函数

  我们已知道,派生类的对象的数据结构是由基类中说明的数据成员和派生类中说明的数据成员共同构成。将派生类的对象中由基类中说明的数据成员和操作所构成的封装体称为基类子对象,它由基类中的构造函数进行初始化。

  构造函数不能够被继承,因此,派生类的构造函数必须通过调用基类的构造函数来初始化基类子对象。所以,在定义派生类的构造函数时除了对自己的数据成员进行初始化外,还必须负责调用基类构造函数使基类数据成员得以初始化。如果派生类中还有子对象时,还应包含对子对象初始化的构造函数。

  派生类构造函数的一般格式如下:

  <派生类名>(<派生类构造函数总参数表>):<基类构造函数>(参数表1),<子对象名>(<参数表2>)
  {
  <派生类中数据成员初始化>
  };

  派生类构造函数的调用顺序如下:

   · 基类的构造函数

   · 子对象类的构造函数(如果有的话)

   · 派生类构造函数

  在前面的例子中,B::B(int i, int j, int k):A(i), bb(j), bbb(k)就是派生类构造函数的定义,下面再举一个构造派生类构造函数的例子。

#include
class A
{
public:
A() { a=0; cout<<"类A的缺省构造函数./n"; }
A(int i) { a=i; cout<<"类A的构造函数./n"; }
~A() { cout<<"类A的析构函数./n"; }
void Print() const { cout< int Geta() { reutrn a; }
private:
int a;
}
class B : public A
{
public:
B() { b=0; cout<<"类B的缺省构造函数./n"; }
B(int i, int j, int k);
~B() { cout<<"类B的析构函数./n"; }
void Print();
private:
int b;
A aa;
}
B::B(int i, int j, int k):A(i), aa(j)
{
b=k;
cout<<"类B的构造函数./n";
}
void B::Print()
{
A::Print();
cout< }

void main()
{
B bb[2];
bb[0] = B(1, 2, 5);
bb[1] = B(3, 4, 7);
for(int i=0; i<2; i++)
bb[i].Print();
}


  2. 构造函数

  当对象被删除时,派生类的析构函数被执行。由于析构函数也不能被继承,因此在执行派生类的析构函数时,基类的析构函数也将被调用。执行顺序是先执行派生类的构造函数,再执行基类的析构函数,其顺序与执行构造函数时的顺序正好相反。这一点从前面讲过的例子可以看出,请读者自行分析。

  3. 派生类构造函数使用中应注意的问题

  (1) 派生类构造函数的定义中可以省略对基类构造函数的调用,其条件是在基类中必须有缺省的构造函数或者根本没有定义构造函数。当然,基类中没有定义构造函数,派生类根本不必负责调用基类的析构函数。

  (2) 当基类的构造函数使用一个或多个参数时,则派生类必须定义构造函数,提供将参数传递给基类构造函数途径。在有的情况下,派生类构造函数的函数体可能为空,仅起到参数传递作用。如本讲第一个例子就属此种情况。

  子类型化和类型适应

  1. 子类型化

  子类型的概念涉及到行为共享,它与继承有着密切关系。

  有一个特定的类型S,当且仅当它至少提供了类型T的行为,由称类型S是类型T的子类型。子类型是类型之间的一般和特殊的关系。

  在继承中,公有继承可以实现子类型。例如:

class A
{
public:
void Print() const { cout<<"A::print() called./n"; }
};
class B : public A
{
public:
void f() {}
};


  类B继承了类A,并且是公有继承方式。因此,可以说类B是类A的一个子类型。类A还可以有其他的子类型。类B是类A的子类型,类B具备类A中的操作,或者说类A中的操作可被用于操作类B的对象。

  子类型关系是不可逆的。这就是说,已知B是A的子类型,而认为A也是B的子类型是错误的,或者说,子类型关系是不对称不。

  因此,可以说公有继承可以实现子类型化。

  2. 类型适应

  类型适应是指两种类型之间的关系。例如,B类型适应A类型是指B类型的对象能够用于A类型的对象所能使用的场合。

  前面讲过的派生类的对象可以用于基类对象所能使用的场合,我们说派生类适应于基类。

  同样道理,派生类对象的指针和引用也适应于基类对象的指针和引用。

  子类型化与类型适应是致的。A类型是B类型的子类型,那么A类型必将适应于B类型。

  子类型的重要性就在于减轻程序人员编写程序代码的负担。因为一个函数可以用于某类型的对象,则它也可以用于该类型的各个子类型的对象,这样就不必为处理这些子类型的对象去重载该函数。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
实验题目1:班级学生学期成绩管理系统 (1)程序功能简介 灵活运用类的继承、对象成员等机制,设计一个能够实现班级学生学期成绩管理的程序。 (2)程序设计说明 ① 个人信息类CPerson的数据成员有姓名、性别、年龄、身份证号等数据成员,成员函数根据需要自行设计; ② 学生类CStudent从CPerson派生,并增加学号、CCourse对象成员数组(大小至少3个)等数据成员,并根据需要自行设计成员函数,包括能够求解所选修课程的总学分、显示某个学生基本信息和课程信息的成员函数; ③ 课程类CCourse包括课程名、学分、分数、任课老师等数据成员,成员函数根据需要自行设计; ④ 班级类CClass的数据成员有班级名称、班级人数、CStudent对象成员数组(大小由构造函数确定)等。本班级类CClass的对象成员数组需要在构造函数中用new动态分配内存空间,在析构函数中用delete释放。在CClass类中设计包括能够求解最高成绩、最低成绩和平均成绩以及通过学号查找并输出某个学生全部信息(例如Seek())等功能在内的成员函数; ⑤ 构造三十个学生的数据,每个学生都有三门课程成绩,输出并显示这些数据; ⑥ 根据类的需要添加适当的其它成员,编写完整的程序并测试。 (3)程序调试运行 运行程序查看结果,并进行源代码调试和优化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值