继承

 类之间的关系 

 继承:在已有类的基础上创建新类的过程

  一个 B 类继承A类,或称从类 A 派生类 B。类 A 称为基类(父类),类 B 称为派生类(子类)

基类和派生类

类继承关系的语法形式:
class 派生类名 : 基类名表
  {
     数据成员和成员函数声明

  };

基类名表  构成

访问控制  基类名1, 访问控制  基类名2 ,… , 访问控制  基类名n

访问控制 表示派生类对基类的继承方式,使用关键字:
  public 公有继承
  private 私有继承

  protected         保护继承

访问控制

不论种方式继承基类,派生类都不能直接使用基类的私有成员 

派生类的生成过程 

派生类的生成过程经历了三个步骤:
       ●吸收基类成员(全部吸收(构造、析构除外),但不一定可见)
       ●改造基类成员

       ●添加派生类新成员

(1)   吸收基类成员:在C++的继承机制中,派生类吸收基类中除构造函数和析构函数之外的全部成员。 

(2)   改造基类成员 :通过在派生类中定义同名成员(包括成员函数和数据成员)来屏蔽(隐藏)在派生类中不起作用的部分基类成员。

(3)   添加新成员 :仅仅继承基类的成员是不够的,需要在派生类中添加新成员,以保证派生类自身特殊属性和行为的实现。 

公有继承

例:

#include<iostream>
using namespace std ;
class A
{ public :
      void  get_XY()   { cout << "Enter two numbers of x, y : " ;  cin >> x >> y ; }
      void  put_XY()    { cout << "x = "<< x << ", y = " << y << '\n' ; }
   protected:    int x, y ;
};
class B : public A
{ public :
      int  get_S() { return s ; };
      void  make_S()  { s = x * y ; };    // 使用基类数据成员x,y
   protected:   int s;
};
class C : public B
{ public : 
      void  get_H()   { cout << "Enter a number of h : " ;  cin >> h ; } 
      int  get_V() { return v ; }
      void  make_V()  { make_S(); v = get_S() * h ; } 	// 使用基类成员函数
   protected:    int h, v;
};
int main()
{ A objA ;
   B objB ;
   C objC ;
   cout << "It is object_A :\n" ;
   objA.get_XY() ;
   objA.put_XY() ;
   cout << "It is object_B :\n" ;
   objB.get_XY() ;
   objB.make_S() ;
   cout << "S = " << objB.get_S() << endl ;
   cout << "It is object_C :\n" ;
   objC.get_XY() ;
   objC.get_H();
   objC.make_V() ;
   cout << "V = " << objC.get_V() << endl ;
}

定义一个基类person(不定义构造函数)

姓名、性别、年龄(访问权限设置为私有)

定义公有的成员函数set_p()

定义公有的成员函数display_p(),显示person的信息

再由基类派生出学生类(不定义构造函数,采用公有继承的方式)

增加学号、班级、专业和入学成绩 

定义公有成员函数set_t()

定义成员函定义公有的成员函数display_s(),显示所有的信息

例:

#include<iostream>
#include <string>
using namespace std;
class Person
{

	string name;
	int age;
	string sex;
public:
	void set_p()	{
		cout<<"name\tage\tsex"<<endl;
		cin>>name>>age>>sex;
	}
	void show_p()	{
 		cout<<name<<"  "<<age<<"  "<<sex<<endl;
	}
};
class student :public Person
{
	string no;
	string zhuanye;
	string t_class;
	float score;
public:
	void set_t(){
  	    set_p(); //调用继承于基类的成员函数访问继承于基类的私有数据成员
	    cout<<"zhuanye\tt_class\tscore"<<endl;
	    cin>>zhuanye>>t_class>>score;
	}
	void show_t()	{
		show_p();
		cout<<zhuanye<<"  "<<t_class<<"  "<<score<<endl;
	}
};

重名成员

派生类定义了与基类同名的成员,在派生类中访问同名成员时屏蔽(hide)了基类的同名成员
在派生类中使用基类的同名成员,显式地使用类名限定符:类名 :: 成员 

重名数据成员

例:

class  base
  { public :
           int  a ,  b ;  
  } ;
class  derived : public  base
  { public :  
         int  b ,  c ; 
  } ;
void  f ()
{ derived  d ;
   d . a = 1 ;
   d . base :: b = 2 ;
   d . b = 3 ;
   d . c = 4 ;
};

例8-4

#include<iostream>	
using namespace std ;
class A
{ public:	  
       int a1, a2 ;
      A( int i1=0, int i2=0 ) { a1 = i1; a2 = i2; }
      void print() 
         { cout << "a1=" << a1 << '\t' << "a2=" << a2 << endl ; }
};
class B : public A
{ public:	
       int b1, b2 ;
       B( int j1=1, int j2=1 ) { b1 = j1; b2 = j2; }
       void print()		//定义同名函数
         { cout << "b1=" << b1 << '\t' << "b2=" << b2 << endl ; }
 void printAB()
        { A::print() ;		//派生类对象调用基类版本同名成员函数
           print() ;		//派生类对象调用自身的成员函数
       }
};
int main()
{ B  b ;        b.A::print();	b.printAB();  }

派生类中访问静态成员

基类定义的静态成员,将被所有派生类共享(基类和派生类共享基类中的静态成员)
根据静态成员自身的访问特性和派生类的继承方式,在类层次体系中具有不同的访问性质 
派生类中访问静态成员,用以下形式显式说明:
                                         类名 :: 成员
或通过对象访问 对象名 . 成员

例:

#include<iostream>
using namespace std ;
class B
{ public:
    static void Add() { i++ ; }
    static int i;
    void out() { cout<<"static i="<<i<<endl; }
};
int B::i=0;
class D : private B
{ public:    
      void f() 
       { i=5;
         Add();
         B::i++;
         B::Add();
       }
};
int main()
{ B x;  D y;
  x.Add();
  x.out();
  y.f();
  cout<<"static i="<<B::i<<endl;
  cout<<"static i="<<x.i<<endl;
  //cout<<"static i="<<y.i<<endl;
}

基类的初始化

在创建派生类对象时用指定参数调用基类的构造函数来初始化派生类继承基类的数据

派生类构造函数声明为:

派生类构造函数 ( 变元表 ) : 基类 ( 变元表 ) , 对象成员1( 变元表 )

… 对象成员n ( 变元表 ) ;

 构造函数执行顺序:基类 → 对象成员→派生类

例8-6  调用构造函数顺序测试,构造函数无参数

#include<iostream>
using namespace std ;
class  Base
  { public :  Base ( ) { cout << "Base created.\n" ;  }
  } ;
class  D_class : public  Base
  { public :  D_class ( ) { cout << "D_class created.\n" ;  }
  } ;
int main ( )
{ D_class d1 ; }

例:

#include<iostream>
using namespace std ;
class  parent_class
{     int  data1 , data2 ;
   public :
       parent_class ( int  p1 , int  p2 ) { data1 = p1; data2 = p2; }
       int  inc1 () { return  ++ data1; }
       int  inc2 () { return  ++ data2 ; }
       void  display  ()  {cout << "data1=" << data1 << " , data2=" << data2 << endl ; }
};
class  derived_class : private  parent_class
{     int  data3 ;
       parent_class  data4 ;
   public:
       derived_class ( int  p1 , int  p2 , int  p3 , int  p4 , int  p5 ): parent_class ( p1 , p2 ) , data4 ( p3 , p4 )
           { data3 = p5 ; }
       
int  inc1 ( ) { return  parent_class :: inc1 ( ) ; }
       int  inc3 ( ) { return  ++ data3 ; }
       void  display ( )
          { parent_class :: display ( ) ;   data4.display ( ) ;
             cout << "data3=" << data3 << endl ;
          }
} ;
int main ( )
{ derived_class  d1 ( 17 , 18 , 1 , 2 , -5 ) ;   d1 . inc1 ( ) ;     d1 . display ( ) ;  }

派生类构造函数和析构函数的定义规则 

派生类构造函数和析构函数的使用原则:
1.基类的构造函数和析构函数不能被继承
2.如果基类没有定义构造函数或有无参的构造函数, 派生类也可以不用定义构造函数
3.如果基类无无参的构造函数,派生类必须定义构造函数
4.如果派生类的基类也是派生类,则每个派生类只负责直接基类的构造
5.派生类是否定义析构函数与所属的基类无关

派生类的构造函数的定义 

派生类的数据成员既包括基类的数据成员,也包括派生类新增数据成员。

如何对基类中的成员进行初始化?


最佳解决方案:调用基类构造函数对基类成员进行初始化。

派生类构造函数的定义

     在C++中,派生类构造函数的一般格式为:
      派生类::派生类名(参数总表):基类名(参数表)
     {
             // 派生类新增成员的初始化语句
     }
     注意:这是基类有构造函数且含有参数时使用

派生类析构函数

(1)当派生类中不含对象成员时
●在创建派生类对象时,构造函数的执行顺序是:基类的构造函数→派生类的构造函数;
●在撤消派生类对象时,析构函数的执行顺序是:派生类的析构函数→基类的析构函数。
(2)当派生类中含有对象成员时
●在定义派生类对象时,构造函数的执行顺序:基类的构造函数→对象成员的构造函数→派生类的构造函数;

●在撤消派生类对象时,析构函数的执行顺序:派生类的析构函数→对象成员的析构函数→基类的析构函数。

例:  察一个考点、圆、圆柱体的层次结构 

class Point
{   friend ostream &operator<< (ostream &, const Point &);
  public:
    Point( int = 0, int = 0 ) ;	// 带默认参数的构造函数
    void setPoint( int, int ) ;	// 对点坐标数据赋值
    int getX() const { return x ; }	    int getY() const { return y ; }
  protected:    int x, y;	// Point类的数据成员
};
class Circle : public Point
{   friend ostream &operator<< (ostream &, const Circle &);	// 友元函数
  public:
    Circle(double r=0.0, int x=0, int y=0);	// 构造函数
    void setRadius(double);  /*置半径*/          double getRadius() const;     /*返回半径*/ 
    double area() const;		// 返回面积
  protected:    double radius;	// 数据成员,半径
};
class Cylinder:public Circle
{    friend ostream & operator<<(ostream &, const Cylinder &);    // 友元函数
   public:
     Cylinder(double h=0.0, double r=0.0, int x=0, int y=0);      // 构造函数
     void setHeight(double);    /* 置高度值*/           double getHeight() const;	    /* 返回高度值*/
     double area() const;	     /* 返回面积*/            double volume() const;	    /* 返回体积*/
   protected:     double height;	// 数据成员,高度
};

多继承

一个类有多个直接基类的继承关系称为 多继承
  多继承声明语法

class  派生类名: 访问控制  基类名1访问控制  基类名2,  … , 访问控制  基类名n

    {

         数据成员和成员函数声明

    }

  C 可以根据访问控制同时继承类 A 和类 B 的成员,并添加自己的成员


多继承的派生类构造和访问

I多个基类的派生类构造函数可以用初始式调用基类构造函数初始化数据成员。
II执行顺序与单继承构造函数情况类似。多个直接基类构造函数执行顺序取决于定义派生类时指定的各个继承基类的顺序。
III一个派生类对象拥有多个直接或间接基类的成员。不同名成员访问不会出现二义性。如果不同的基类有同名成员,派生类对象访问时应该加以识别。 

例题:

class Base1
{ public:
      Base1(int x) { value = x ; }
      int getData() const { return value ; }
   protected:
      int value;
};
class Base2
{ public:
      Base2(char c) { letter=c; }
      char getData() const { return letter;}
   protected:
      char letter;
};
class Derived : public Base1, public Base2
{    friend ostream &operator<< ( ostream &, const Derived & ) ;
   public :
      Derived ( int, char, double ) ;
      double getReal() const ;
   private :
      double real ;
};
int main()
{ Base1 b1 ( 10 ) ;
   Base2 b2 ( 'k' ) ;
   Derived d ( 5, 'A', 2.5 ) ;
      :
   return ;
}

多继承的构造函数:

派生类名(参数总表):基类名1(参数表1),基类名2(参数表2),…,基类名n(参数表n)

   {

           // 派生类新增成员的初始化语句

    

多继承方式下构造函数的执行顺序:

执行所有基类的构造函数

执行对象成员的构造函数

最后执行派生类的构造函数

处于同一层次的各基类构造函数的执行顺序取决于定义派生类时所指定的基类顺序与派生类构造函数中所定义的成员初始化列表顺序没有关系。内嵌对象成员的构造函数执行顺序与对象在派生类中声明的顺序一致

多继承的析构函数 

析构函数名同样与类名相同,无返回值、无参数,而且其定义方式与基类中的析构函数的定义方式完全相同。

功能是在派生类中对新增的有关成员进行必要的清理工作。

析构函数的执行顺序与多继承方式下构造函数的执行顺序完全相反,首先对派生类新增的数据成员进行清理,再对派生类对象成员进行清理,最后才对基类继承来的成员进行清理。   

赋值兼容规则 

赋值兼容规则指在程序中需要使用基类对象的任何地方,都可以用公有派生类的对象来替代。

赋值兼容规则中所指的替代包括以下的情况:

 a派生类的对象可以赋给基类对象

 b派生类的对象可以初始化基类的引用

 c派生类的对象的地址可以赋给基类类型的指针

赋值兼容应注意的问题

1)声明为指向基类的指针可以指向它的公有派生类的对象,但不允许指向它的私有派生类的对象。例如:

   class B  {…}

   class DprivateB {…}

   B b1,*pblD d1

    pb1=&b1;        //合法,基类B的对象b1B类的指针

      pb1=&d1;        //非法,不允许将基类指针指向它的私有派生类对象

2)允许将一个声明为指向基类的指针指向其公有派生类对象,但是不能将一个声明为指向派生类对象的指针指向其基类的一个对象。

  (3) 声明为指向基类对象的指针,当其指向公有派生类对象时,只能用它来直接访问派生类中从基类继承来的成员,而不能直接访问公有派生类的定义的成员

心得体会:

继承的使用,大大缩短代码长度,从而使程序变得更简洁。但要注意的是,在最初写程序的时候,是不考虑继承的,一开始按着自己设计的思路写代码,写完运行无误后,在观察分析是否可以用继承。继承对程序在一般情况下只是起修饰美化的作用,对程序中功能的具体实现无太大作用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值