继承

一、类之间的关系 

  继承:已有类的基础上创建新类的过程
   一个 B 类继承A类,或称从类A派生类B

   A 称为基类(父类),类B称为派生类(子类)

二、基类和派生类

  类继承关系的语法形式

  class 派生类名: 基类名表

   {

        数据成员和成员函数声明

   };

  基类名表 构成

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

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

    public  公有继承

    private    私有继承

    protected  保护继承

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

派生类的生成过程 

派生类的生成过程经历了三个步骤:

       ●吸收基类成员(全部吸收(构造、析构除外),但不一定可见

       改造基类成员

       ●添加派生类新成员

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

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

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

对于下面的继承关系:

classFather

{

  int a,b;

public:

//成员函数

};

classSon:publicFather

{

  int c;

public:

  // 成员函数

};
#include <iostream>
using namespace std;
class A{
public:
	int a;
	int b;
private:
	int c;
protected:
	int d;
};
class B: public A
{ int c; };
main( ){
	cout << ” size of A is” << sizeof(A);
	cout << ” size of B is” << sizeof(B); }
#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;
};
1.公有继承

#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;
};
定义一个基类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;
	}
};

该问题的第二种解决方法Student_person3

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

	string name;
	int age;
	string sex;
public:
	void set()	{
		cout<<"name\tage\tsex"<<endl;
		cin>>name>>age>>sex;
	}
	void show()	{
 		cout<<name<<"  "<<age<<"  "<<sex<<endl;
	}
};
class student :public Person
{
	string no;
	string zhuanye;
	string t_class;
	float score;
public:
	void set(){    //隐藏了基类中的同名成员       
	     Person::set(); //调用继承于基类的成员函数访问继承于基类的数据成员
   	     cout<<"zhuanye\tt_class\tscore"<<endl;
	     cin>>zhuanye>>t_class>>score;
	}
	void show()	{
		Person::show();
		cout<<zhuanye<<"  "<<t_class<<"  "<<score<<endl;
	}
};

重名成员

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

类名:: 成员

1.重名数据成员

例:
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 ;
};
#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.派生类是否定义析构函数与所属的基类无关

派生类的构造函数的定义 

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

问题1:如何对基类中的成员进行初始化?


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


派生类构造函数的定义

    在C++,派生类构造函数的一般格式为:

      派生类::派生类名(参数总表):基类名(参数表)

     {

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

     }

     注意:这是基类有构造函数且含有参数时使用

派生类析构函数 

1)当派生类中不含对象成员时
        在创建派生类对象时,构造函数的执行顺序是:基类的构造函数→派生类的构造函数;
        在撤消派生类对象时,析构函数的执行顺序是:派生类的析构函数→基类的析构函数。
2)当派生类中含有对象成员时
         在定义派生类对象时,构造函数的执行顺序:基类的构造函数→对象成员的构造函数→派生类的构造函数;
         在撤消派生类对象时,析构函数的执行顺序:派生类的析构函数→对象成员的析构函数→基类的析构函数。
class B
{
public:
    B() { cout<<"B()"<<endl; }
    ~B() { cout<<"~B()"<<endl; }
};
class D : public B
{
public:
    D(){ cout<<"D()"<<endl; }
    ~D() { cout<<"~D()"<<endl; }
};
int main()
{
    D d;
    return 0;
}

多继承

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

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

    {

         数据成员和成员函数声明

    }

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

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

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

多继承的构造函数

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

   {

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

    }

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

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

●先执行所有基类的构造函数

●再执行对象成员的构造函数

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

处于同一层次的各基类构造函数的执行顺序取决于定义派生类时所指定的基类顺序

与派生类构造函数中所定义的成员初始化列表顺序没有关系。

内嵌对象成员的构造函数执行顺序与对象在派生类中声明的顺序一致

多继承的析构函数 

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

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

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

赋值兼容规则 

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

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

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

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

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

赋值兼容的可行性

通过公有继承,
    派生类得到了除了构造、析构函数以外的所有成员
    且这些成员的访问控制属性也和基类完全相同。
    这样,它便具备了基类的所有功能。
利用赋值兼容规则
     派生类的对象可以赋给基类对象(强制类型转换)
     派生类的对象可以初始化基类的引用
     派生类的对象的地址可以赋给基类类型的指针

例如,下面声明的两个类:

class Base{

      

};

class Derived:public Base{

};

    根据赋值兼容规则, 以下几种情况是合法的

(1) 可以用派生类对象给基类对象赋值。例如:

      Base b;

      Derived d;

      b=d;

         这样赋值的效果是,对象b中所有数据成员都将具有对象d中对应数据成员的值。

(2) 可以用派生类对象来初始化基类的引用。例如:

     Derived d;

     Base &br=d;

(3) 可以把派生类对象的地址赋值给指向基类的指针。例如:

   Derived d;

   Base *bptr=&d;

         这种形式的转换,是在实际应用程序中最常见到的。

(4) 可以把指向派生类对象的指针赋值给指向基类对象的指针。例如:

    Derived *dptr,obj; dptr=&obj;

    Base *bptr=dptr;

赋值兼容规则的特点
在替代之后,派生类对象就可以作为基类的对象使用,但只能使用从基类继承的成员。

一个派生类对象也是一个基类对象,一个基类对象可派上用场的地方,派生类对象一样可派上用场。反之则不然。

赋值兼容应注意的问题

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

   class B  {…}

   class DprivateB {…};

   B b1,*pblD d1

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

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

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

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


心得体会:

继承不是在写代码之前就想好的,需要在写代码的过程中能用到的时候再去补充的,或者是在代码完成后优化的,这样子可以实现代码的复用,提高代码的简洁性。




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值