C++之——继承

目录

继承的概念

继承相关概念

类继承的语法格式

继承方式

关键字

公有继承图解

注意事项

派生类的生成过程

三个步骤

(1) 吸收基类成员

(2)   改造基类成员

(3)   添加新成员

派生类对象

图解

 实例

覆盖(屏蔽)

定义

实例

现象解释

公有继承实战演练

代码示例

图解分析

重名数据成员

重名函数

派生类中访问静态成员

 基类定义的静态成员

 派生类中访问静态成员

基类的初始化

实例

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

派生类构造函数定义

图解

    格式

派生类析构函数

函数的调用顺序

实例二

 继承的应用


继承的概念

继承指的是在已有类的基础上,创建新类的过程。

继承相关概念

一个B类继承A类,又可以称为类A派生出类B

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

注意:基类和派生类是一对名词,父类和子类是一对名词,二者不要混为一谈。

类继承的语法格式

class 派生类名 : 基类名表
 	{
	      数据成员和成员函数声明
 	};
基类名表  构成
	访问控制  基类名1, 访问控制  基类名2 ,… , 访问控制  基类名n

继承方式

关键字

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

●public 公有继承

●private 私有继承

●protected    保护继承

公有继承图解

由图片可以知道,左侧是基类,右侧是派生类

 派生类将基类的一切成员都继承过来了(私有成员也继承过来了,也给他分配空间了,只是不能调用),蓝色区域是派生类新增的数据成员。

注意事项

不论种方式继承基类,派生类都不能直接使用基类的私有成员(可以继承,但继承后不能使用)

派生类的生成过程

三个步骤

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

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

       ●改造基类成员

       ●添加派生类新成员

(1) 吸收基类成员

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

(2)   改造基类成员

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

(3)   添加新成员

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

派生类对象

图解

 实例

class Father
{
  int a,b;
public:
 // 成员函数
};

class Son:public Father
{
  int c;
public:
  // 成员函数
};

该代码中Son是Father的派生类,此时它具有Father类中的一切数据成员, a,b是他的原有数据成员(继承过来的),而c是他新增的数据成员。(注意本处的继承方式是公有继承,也就是子类从父类继承过来的数据成员的访问权限没有变动

覆盖(屏蔽)

定义

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

实例

#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); }

运行结果:

现象解释

此处的类B继承了类A,而在类B中定义了一个新的私有成员c,这里的c和继承过来的c同名,从而使得类B中的c覆盖了类A中的c,即使这样,这两个类中的c仍然不能混为一谈,系统仍然会给每一个c分配自己的空间,这也就使得A和B的size不同,B比A多了一个4字节。

此外,再来解释一下这里的20是怎么出来的:类B从类A继承过来了4个int类型的数据成员,这就是4*4=16.再加上类B自身的一个数据成员,也就是再加上4,就是16+4=20了

公有继承实战演练

代码示例

#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 ;
}

图解分析

运行结果:

 

三重继承,类 A派生出了类B,类B派生出了类C

继承方式均为公有继承。

重名数据成员

 派生类定义了与基类同名的成员,在派生类中访问同名成员时屏蔽(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 ;
};

此处的d.base::b和d.b所指的变量不是同一个变量

图解如下:

重名函数

#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();  }

运行结果如下:

 需要注意的是派生类对象这里使用了A中的print函数写出来就是A::print( );

派生类中访问静态成员

 基类定义的静态成员

1、将被所有派生类共享(基类和派生类共享基类中的静态成员)

2、根据静态成员自身的访问特性和派生类的继承方式,在类层次体系中具有不同的访问性质

 派生类中访问静态成员

用以下形式显式说明:

            类名 :: 成员

 或通过对象访问

            对象名 . 成员

#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 ( 变元表 ) ; 构造函数执行顺序:基类->对象成员->派生类

实例

#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 ;
  return 0;
 }

 通过运行结果可以得知,系统先调用了Base的构造函数,然后又调用了D_class的构造函数

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

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

派生类构造函数定义

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

图解

 

    格式

      在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;
}

运行结果(函数调用顺序)

 分析:析构函数的调用顺序恰好与构造函数的调用顺序相反

构造函数的调用顺序是先调用基类的,再调用派生类的。

析构函数的调用顺序是先调用派生类的,再调用基类的。

实例二

#include<iostream.h>
class base
{
public:
	base(){cout<<"constructing base class"<<endl;}
	~base(){cout<<"destructing base class"<<endl; }
};
class subs:public base
{
public:
	subs(){cout<<"constructing sub class"<<endl;}
	~subs(){cout<<"destructing sub class"<<endl;}
};
void main()
{
	subs s;
} 

 继承的应用

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

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;	// 数据成员,高度
};
// Point 类的成员函数 

// 构造函数,调用成员函数对 x,y作初始化
Point::Point ( int a, int b ) 
    { setPoint ( a , b ) ; } 
// 对数据成员置值
void Point :: setPoint ( int a, int b )  { x = a ;  y = b ; }
// 重载插入算符,输出对象数据
ostream &operator<< ( ostream &output , const Point &p )
{ output << '[' << p.x << "," << p.y << "]"  ;
     return output ;
}
// Circle 类的成员函数 
// 带初始化式构造函数,首先调用基类构造函数
Circle::Circle( double r, int a, int b ): Point( a, b )  { setRadius ( r ); }
// 对半径置值
void Circle::setRadius ( double r )  { radius = ( r >= 0 ? r : 0 ); }
// 返回半径值
double Circle::getRadius() const { return  radius; }
// 计算并返回面积值
double Circle::area() const  { return  3.14159 * radius * radius ; }
// 输出圆心坐标和半径值
ostream & operator<< ( ostream &output, const Circle &c)
{ output << "Center = " << '[' << c.x << "," << c.y << "]" << "; Radius = "
    << setiosflags(ios::fixed|ios::showpoint) << setprecision(2) << c.radius ;
   return  output ;
} 
Cylinder::Cylinder(double h, double r, int x, int y):Circle(r,x,y)  { setHeight(h); }
void Cylinder::setHeight(double h)  { height = ( h >= 0 ? h : 0 ); }
double Cylinder::getHeight() const { return height; }
double Cylinder::area()const{ return  2*Circle::area()+2*3.14159*radius*height; }
double Cylinder::volume() const  { return  Circle::area()*height; }
ostream &operator<< ( ostream &output, const Cylinder &cy )
{ output << "Center = " << '[' << cy.x << "," << cy.y << "]" << "; Radius = "
                << setiosflags(ios::fixed|ios::showpoint) << setprecision(2) << cy.radius
                << "; Height = " << cy.height << endl ;
     return output;
} 
#include<iostream>
using namespace std ;
#include <iomanip.h>
int main(){
   Point p ( 72, 115 ) ;		//定义点对象并初始化
   cout << "The initial location of p is " << p << endl ;
   p.setPoint ( 10, 10 ) ;		//置点的新数据值
   cout << "\nThe new location of p is " << p << endl ;	//输出数据
   Circle c ( 2.5, 37, 43 ) ;	//定义圆对象并初始化
   cout<<"\nThe initial location and radius of c are\n"<<c<<"\nArea = "<<c.area()<<"\n" ;
   c.setRadius ( 4.25 ) ;    c.setPoint ( 2, 2 ) ;
   cout<<"\nThe new location and radius of c are\n"<<c<<"\nArea = "<<c.area()<< "\n" ;
   Cylinder cyl ( 5.7, 2.5, 12, 23 ) ;	//定义圆柱体对象并初始化
cout << "\nThe initial location, radius ang height of cyl are\n" << cyl
      << "Area = " << cyl.area() << "\nVolume = " << cyl.volume() << '\n';
   cyl.setHeight ( 10 ) ;   cyl.setRadius ( 4.25 ) ;    cyl.setPoint ( 2, 2 ) ;
   cout << "\nThe new location, radius ang height of cyl are\n" << cyl
        << "Area = " << cyl.area() << "\nVolume = "<<cyl.volume()<< "\n" ;
} 

希望对大家有所帮助!!!加入对各位有帮助,还望各位点个赞再走。 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

牛哥带你学代码

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值