继承(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 ; }