第七章--继承与派生

来源:互联网 发布:手机淘宝如何延长收货 编辑:程序博客网 时间:2024/05/16 03:05

一、派生类的声明方式

声明派生类的方法:
class 派生类名:[ 继承方式 ] 基类名
{ 派生类新增的成员
};
继承方式:
Public:公用方式;
Private:私有方式;
Protected:受保护方式。
继承方式默认为私有方式。

不同继承方式的影响主要体现在:
派生类成员对基类成员的访问权限
通过派生类对象对基类成员的访问权限

二、派生类的构成
派生类的成员:从基类继承过来的成员、自己增加的成员。
从基类继承过来的成员:体现基类与子类的共性;
新增加的成员:体现了子类的个性。
注意:类的继承并不是把基类成员和派生类新增成员简单地放在一起。构造一个派生类包括以下四部分工作:

1、从基类接收成员:派生类把基类的全部成员(不包括构造函数和析构函数)接收过来。不足之处在于会造成冗余,浪费存储空间和执行效率,尤其是多次派生时。不要随意地找一个类去派生出某一个子类。
注意:基类的构造函数和析构函数不能继承。
2、调整从基类接收的成员:接收基类成员必须照单全收,但编程人员可以对接收过来的成员作一些调整。如:
(1)通过指定不同的继承方式,来改变基类成员在派生类中的访问属性;
(2)在派生类中声明一个和基类成员同名的成员,来“遮盖”接收的基类成员。如果想遮盖成员函数,其函数名和参数表必须相同,否则成为重载。
3、在声明派生类时增加新的成员:主要体现子类对基类的扩展。
4、构造派生类的构造函数和析构函数:因为构造和析构函数不能从基类继承。

三、公有继承(public)
1.基类的public和protected成员的访问属性在派生类中保持不变,但基类的private成员不可直接访问。
2.派生类中的成员函数可以直接访问基类中的public和protected成员,但不能直接访问基类的private成员。
3.通过派生类的对象只能访问基类的public成员。

#include <iostream.h>    //补充举例7-3#include <string.h>class person   // 基类{public:    void get_value( )    { cin>>num>>name; }    void display( )    { cout<<“id:”<<num<<endl;      cout<<“name:”<<name<<endl; }private:    int num;    string name;};class student : public person   // 派生类{public:    void display( ) //与基类同名函数    {cout<<“id:”<<num<<endl;       // 错误, 派生类中不能访问基类私有成员     cout<<“name:”<<name<<endl; // 错误, 派生类中不能访问基类私有成员     cout<<“stu_id:”<<stu_id<<endl; }private:    int stu_id;};

四、私有继承(private)
1。基类的public和protected成员都以private身份出现在派生类中,但基类的private成员不可直接访问。
2、派生类中的成员函数可以直接访问基类中的public和protected成员,但不能直接访问基类的private成员。
3、通过派生类的对象不能直接访问基类中的任何成员。

#include <iostream>#include <string>using namespace std;class person  // 基类{public:    void get_value( )    { cin>>num>>name; }    void person_display( ) { cout<<<<num<<endl;      cout<<name<<endl; }private:    int num;    string name;};class student : private person{public:    void student_display( )    { cout<<stu_id<<endl; }private:    int stu_id;};void  main( ){ student zhangsan;zhangsan.person_display( );   // 私有继承基类的公用函数   // 变成子类私有函数,类外不能访问zhangsan.student_display( );}PS:修改:可以在派生类内调用基类的公用函数

修改:

#include <iostream>#include <string>using namespace std;class person{public: void get_value( )    {cout<<"input id,name:";    cin>>num>>name; }  void person_display( )  { cout<<"id:"<<num<<endl;      cout<<"name:"             <<name<<endl;     } private:    int num;    string name;};class student : private person{public:    void student_display( )    { person_display( );       cout<<"stu_id:“              <<stu_id<<endl;    }    void student_cin( )    { get_value( );       cout<<"input stu_id:";      cin>>stu_id;    }private:    int stu_id; };void  main( )  {  student zhangsan;       zhangsan.student_cin( );          zhangsan.student_display( );}

五、保护继承(protected)
1。基类的public和protected成员都以protected身份出现在派生类中,但基类的private成员不可直接访问。
2。派生类中的成员函数可以直接访问基类中的public和protected成员,但不能直接访问基类的private成员。
3。通过派生类的对象不能直接访问基类中的任何成员

对建立其所在类对象的模块来说,它与 private 成员的性质相同。
对于其派生类来说,它与 public 成员的性质相同。
既实现了数据隐藏,又方便继承,实现代码重用

#include <iostream>#include <string>using namespace std;class person{protected:    int id;    string name;};class student : protected person{public:    void student_display( )    { cout<<"id:"<<id<<endl;      cout<<"name:"<<name<<endl;      cout<<"stu_id:"              <<stu_id<<endl;    }private:    int stu_id; };void  main( ){ student zhangsan;  zhangsan.id=1234;  //错误,外界不能访问保护成员  zhangsan.student_display( );     // 合法,此函数是公用函数}

小结 :三种继承方式下派生类成员的访问属性列表如下
这里写图片描述
说明:
①无论哪一种继承方式,在派生类中不能访问基类的私有成员,私有成员只能被本类的成员函数访问;
②多级派生若都采用公用继承,直到最后一级派生类都能访问基类的公用成员和保护成员;
③多级派生若都采用私有继承,经过若干次派生后,基类的所有成员都变成不可访问;
④多级派生若都采用保护继承,派生类外无法访问派生类中的任何成员。
注意:实际应用中,通常采用公用继承方式。

六、继承时的构造函数
1。基类的构造函数不被继承,派生类中需要声明自己的构造函数。
定义构造函数时,只需要对本类中新增成员进行初始化,对继承来的基类成员的初始化,自动调用基类构造函数完成。
2。派生类的构造函数需要给基类的构造函数传递参数

派生类构造函数任务:
① 初始化基类数据成员;
② 初始化派生类新增数据成员。
1、简单派生类的构造函数
“简单”是指:一个派生类只有一个基类,在派生类的数据成员中不包含基类的对象(即子对象)。
简单派生类的构造函数:
派生类构造函数名 (总参数表列) :基类构造函数名(参数表列)
{ 对派生类中新增数据成员初始化语句 }
注意:总参数表列包含基类构造函数的参数和派生类新增数据成员初始化所需的参数。

#include <iostream>#include <string>using namespace std;class person  // 基类声明{public:    person (int i, string nam)      //基类构造函数    { id = i;      name = nam;    }protected:    int id;    string name;};class student : public person     // 声明公用派生类{ public:   student( int i, string nam, int sid) :          person( i, nam)       { stuid = sid; } // 子类构造函数   void show( )       {cout<<"id:"<<id<<endl;        cout<<"name:"<<name<<endl;        cout<<"stuid:"<<stuid<<endl;  }   private:    int stuid;};void main( ){  student s1(1234,"张三",200701);          s1.show( );}

将派生类的构造函数形式与“用参数初始化表对数据成员初始化”的对照:
student( int i, string nam, int sid) : person( i, nam) { stuid = sid; }
box(int h, int w, int len): height (h), width (w), length (len) { }
例如:将上一个示例的子类构造函数改写如下:
student( int i, string nam, int sid) :
person( i, nam),stuid(sid) { }
这样,函数体为空,显得很简捷,方便。

在建立一个对象时,执行构造函数的顺序是:
①先调用基类构造函数;
②再执行派生类自身的构造函数。
如上例:先初始化 i、nam,再初始化 stuid。

2、有子对象的派生类的构造函数
如果类的数据成员除了标准数据类型外,还包含类对象,这种被包含的对象就叫子对象(subobject ),即对象中的对象。

    如学生类:在person的派生类student中,增加学号stuid、家长parent,而家长本身也属于 person 类型,这样,家长parent 项既是基类 person 对象,也派生类student 的子对象。

派生类构造函数任务:
1)初始化基类数据成员;
2)初始化子对象数据成员;
3)初始化派生类新增数据成员。

派生类构造函数形式:
派生类构造函数名(总参数表) :
基类构造函数名(参数表列),子对象名(参数表列),
{ 派生类中新增数据数据成员初始化语句 }

执行派生类构造函数的顺序:
调用基类构造函数,初始化基类数据成员;
调用子对象构造函数,初始化子对象数据成员;
执行派生类自身的构造函数,初始化自己新增数据成员。

如 student 派生类构造函数如下:    student( int i, string nam, int pid, string pnam, int  sid ) :         person ( i, nam),    parent (pid,pnam)         {stuid= sid; }

注意:派生类构造函数参数个数等于基类构造函数参数、子
对象构造函数参数、自己新增数据对象初始化参数之和。
这种形式十分常见。

同名隐藏规则:
1.当派生类与基类中有相同成员时:
若未强行指名,则通过派生类对象使用的是派生类中的同名成员。
2.如要通过派生类对象访问基类中被隐藏的同名成员,应使用基类名限定。

多重继承:
如果类A、B是从同一个基类派生的,如下图示:

#include <iostream>#include <string>using namespace std;class N{public:    int a;    void display( )    { cout<<"N::a:"  <<a<<endl; }};class A : public N{public:  int a1;};class B : public N{public:   int a2;};class C :      public A, public B{public:    int a3;    void show( )    {cout<<"a3:"            <<a3<<endl; }};void main( ){ C  c1;   c1.A::a=3;   c1.A::display( );}

基类N的成员a和display( )在一级派生类A、B中都存在。若在main()中访问A类中的a和display( ),必须加上类名限定。

七、虚基类
如果一个派生类有多个直接基类,而这些直接基类又有一个共同的基类,那么在最终的派生类中会保留该间接共同基类数据成员的多份同名成员。前一个示例就是这种情况。

虚基类的作用:在继承间接共同基类时,内存里只存储一份成员。
虚基类定义的格式: (定义派生类时)
class 派生类名:virtual 继承方式 基类名
即在声明派生类时,将关键字 virtual 加在继承方式前面。经过这样的声明后,当基类通过多条派生路径被一个派生类中只继承一次,即基类成员只保留一次。

1)虚基类的初始化:在最后的派生类中,不仅要初始化直接基类,而且还要初始化虚基类。
如果虚基类中定义了带参数的构造函数,且没有定义默认构造函数,则在所有直接派生和间接派生类中通过构造函数的初始化表对虚基类进行初始化。
例如:在定义多重继承的派生类
D时,由于虚基类在派生类中只
有一份数据成员,所以这份数据
成员的初始化必须由最后的派生
类直接给出,以避免出现B、C
的构造函数给出不同的初始化参
数而产生矛盾。

class A{ A (int i) { }};class B : virtual public A{ B (int n) : A(n) { }};class C: virtual public A{ C(int n) : A(n) { }};class D: public B, public C{ D(int n): A(n), B(n), C(n) { }};  // C++中,只有最后的派生的构造函数有效。
原创粉丝点击