C++程序设计基础之(第八章)继承

继承(inheritance)是面向对象程序设计中软件重用的关键技术。继承机制使用已经定义的类作为基础建立新的类定义,新的类是原有类的数据及操作与新类所增加的数据及操作的组合。新的类把原有类的作为基类引用,而不需要修改原有类的定义。新定义的类作为派生类引用。这种可扩充、可重用技术大大降低了大型软件的开发难度。
本章讨论面向对象程序设计中关于继承的概念及其在C++中的实现方法。

8.1 类之间的关系

它们之间有三种主要关系:has-A,uses-A 和 is-A

  • has-A 包含关系,用以描述一个类由多个“部件类”构成。实现has-A关系用类成员表示,即一个类中的数据成员是另一种已经定义的类。
  • uses-A 一个类部分地使用另一个类。通过类之间成员函数的相互联系,定义友元或对象参数传递实现。
  • is-A 机制称为“继承”。关系具有传递性,不具有对称性。

比如:在这里插入图片描述

  • 继承 是类之间定义的一种重要关系
  • 一个 B 类继承A类,或称从类 A 派生类 B
    类 A 称为基类(父类),类 B 称为派生类(子类)
    在这里插入图片描述

8.2 基类和派生类

类继承关系的语法形式

class 派生类名 : 基类名表
{
      数据成员和成员函数声明
};

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

表示派生类对基类的继承方式,使用关键字:
public 公有继承
private 私有继承
protected 保护继承

8.2.1 访问控制

在这里插入图片描述

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

对于以上代码的类关系如下图所示:
在这里插入图片描述
在这里插入图片描述

2.私有继承

在这里插入图片描述
例: 私有继承的测试

#include<iostream>
using namespace std ;
class A
{ public :
      void  get_XY()  { cout << "Enter two numbers of x and y : " ;  cin >> x >> y ; }
      void  put_XY()    { cout << "x = "<< x << ", y = " << y << '\n' ; }
  protected:    int x, y ;
};
class B : private A
{ public :
      int  get_S() { return s ; }
      void  make_S()  { get_XY();    s = x * y ;  }	//get_XY()调用基类成员函数 s=x*y;访问私有数据成员
   private:    int s ;
};
int main()
{ B objB ;	
   cout << "It is object_B :\n" ;
   objB.make_S() ;
   cout << "S = " << objB.get_S() << endl ;
 }

结果为:
在这里插入图片描述
例 :私有数据成员的测试

#include<iostream>
using namespace std ;
class A
{ public: 	 A(){ x=1; }
	 int out() {return x ; }
	 void addX() { x++; }
   private: int x ;
} ;
class B : public A
{ public:	B(){ y=1; }
	int out() {return y ; }
	void addY() { y++; }
  private:	int y ;
} ;
int main()
{ A a ;
   cout << "a.x=" << a.out() << endl ;
   B b ;
   b.addX() ;    b.addY() ;
   cout << "b.x=" << b.A::out() << endl ;
   cout << "b.y=" << b.out() << endl ;
}

基类的私有数据成员不能在派生类中直接访问但派生类对象建立私有数据空间。
在这里插入图片描述

3.保护继承

在这里插入图片描述

8.2.2 重名成员

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

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 ;
};
  • 基类成员的作用域延伸到所有派生类
  • 派生类的重名成员屏蔽基类的同名成员

2.重名成员函数

#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();//派生类对象调用从基类继承的同名成员函数,基类this指针指向派生类对象
  b.printAB();
 }

在这里插入图片描述
通过这个例子,我们可以看出
通过继承,类B具有两个同名成员函数
void A::print(); // void print( A * this );
void B::print(); // void print( B * this );

派生类也是基类,基类指针可以指向派生类对象
派生类中定义与基类同名的成员函数,称为重载成员函数

8.2.3 派生类中访问静态成员

在这里插入图片描述
例 在派生类中访问静态成员

#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;//i 是类D的私有静态数据成员类中可见
         Add();//Add()是类D的私有静态成员函数类中可调用:
         B::i++;//访问B类的静态成员
         B::Add();
       }
};
int main()
{ B x;  D y;
  x.Add();
  x.out();
  y.f();
  cout<<"static i="<<B::i<<endl;//访问B类的静态数据成员
  cout<<"static i="<<x.i<<endl;//通过B类对象访问静态数据成员
  //cout<<"static i="<<y.i<<endl;//错误,i 是类D的私有静态数据成员
}

在这里插入图片描述

8.3 基类的初始化

在这里插入图片描述
例: 调用构造函数顺序测试,构造函数无参数

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

在这里插入图片描述

8.4 继承的应用实例

例:考察一个点、圆、圆柱体的层次结构
在这里插入图片描述

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::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" ;
} 

例:类继承和类包含的比较
//用继承方式设计Point类和Circle类

#include<iostream>
using namespace std ;
class Point
{ public :
      Point(double t1, double t2)  { x=t1; y=t2;}
      void OutPoint() { cout << "Point: x=" << x << " y=" << y << endl ; }
  protected :    double x, y;
};
class Circle : public Point		//Circle类继承Point类
{ public:
       Circle(double t1,double t2, double t3)  :  Point(t1,t2)	 { radius = t3 ; }
       void OutCircle()
           {   Point::OutPoint();      cout << "radius=" << radius << endl ;  }
  protected:    double radius;	//派生类数据成员
};
int main()
{ Circle c( 0, 0, 12.5 ) ;
  c.OutPoint() ;	//调用从基类Point继承的成员函数
  c.OutCircle() ;	//调用Circle类成员函数
} 
//用包含方式设计Point类和Circle类 
#include<iostream>
using namespace std ;
class Point
{ public :
      Point(double t1, double t2)  { x=t1; y=t2;}
      void OutPoint() { cout << "Point: x=" << x << " y=" << y << endl ; }
  protected :    double x, y;
};
class Circle 
{ public:
       Circle(double t1,double t2, double t3)  :  centre(t1,t2)    { radius = t3 ; }
       void OutCircle()
           {   centre.OutPoint();      cout << "radius=" << radius << endl ;  }
       Point  centre;		 //包含Point成员
  protected:    double radius;	
};
int main()
{ Circle c( 0, 0, 12.5 ) ;
  c.centre.OutPoint() ;	//通过成员centre调用Point的成员函数
  c.OutCircle() ;		//调用Circle类成员函数
} 

8.5 多继承

在这里插入图片描述

类 C 可以根据访问控制同时继承类 A 和类 B 的成员,并添加自己的成员
在这里插入图片描述

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

在这里插入图片描述

多继承的简单应用

在这里插入图片描述

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

在这里插入图片描述

8.5.2 虚基类

如果一个派生类从多个基类派生,而这些基类又有一个共同的基类,则在对该基类中声明的名字进行访问时,可能产生二义性。
例如:

class  B  { public : int  b ;} ;
class  B1 : public  B { private : int  b1 ; } ;
class  B2 : public  B { private : int  b2 ; } ;
class  C : public  B1 , public  B2 
    { public : int  f ( ) ;  private : int  d ; } ;

在这里插入图片描述
有:

C  c ;
c . B ;		// error
c . B :: b ;	// error,从哪里继承的?
c . B1 :: b	 ;	// ok,从B1继承的
c . B2 :: b	 ;	// ok ,从B2继承的
#include<iostream>
using namespace std ;
int main ()	
{ C  c ;
  c . B1 :: b = 5 ;       c . B2 :: b = 10 ;
  cout << "path B1==> " << c . B1 :: b << endl ;
  cout << "path B2==> " << c . B2 :: b << endl ;
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
例如:

class  B  { public : int  b ;} ;
class  B1 : virtual  public  B { private : int  b1 ; } ;
class  B2 : virtual  public  B { private : int  b2 ; } ;
class  C : public  B1 , public  B2 
    { private : float  d ; } ;

在这里插入图片描述
有:

C  cc ;
cc . b		// ok

在这里插入图片描述
在这里插入图片描述
例:虚继承的测试

#include<iostream>
using namespace std ;
class  A
{ public :
       A ( ) { cout << "class A" << endl ; } 
} ;
class  B :  public  A
{ public :  
       B ( ) {cout << "class B" << endl ;  } 
} ;
class  C :  public  A
{ public :
       C ( ) {cout << "class C" << endl ;  }
} ;
class  D :  public  B ,  public  C
{ public :  
       D ( ) {cout << "class D" << endl ; } 
} ;
int main ( )
{ D  dd  ;  }

在这里插入图片描述

小结

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值