一.类之间的关系
一个大的应用程序,通常由多个类构成,类与类之间相互协调工作。本节对继承做学习总结。
二.基类和派生类
1.语法规则
class派生类名:基类名表
{
数据成员和成员函数说明
};
其中,基类名表语句格式如下:
访问控制 基类名1,访问控制 基类名2,……访问控制 基类名n
如果一个“基类名表”中只有一个基类,称为单继承;如果“基类名表”中有多个基类,则称为多继承。
派生类举例:
见例一;
- 访问控制分类
1. public公有继承
2. private私有继承
3. protected保护继承
2.访问控制
- 派生类公有继承基类时,基类中所有成员成为派生类的公有成员,基类中保护成员成为派生类的保护成员;派生类私有继承
一个基类时,基类中所有成员成为派生类的保护成员;派生类保护继承一个基类时,基类中所有成员成为派生类的保护成员。
- 不论以何种方式继承基类,派生类都不能直接使用基类的私有成员。
2.1公有继承
以公有方式继承的派生类,基类的成员性质不变,派生类可以使用基类中定义的public和protected成员;并且,基类的公有
成员也是派生类对象的接口,可以在类模块之外被访问。
举例:
见例二
注:私有继承和保护继承在这里不做研究。
2.2重名成员
C++允许派生类的成员与基类成员重名。在派生类中访问重名成员时,屏蔽基类的同名成员。如果要在派生类中使用基类的
同名成员,可以显示地使用作用域符指定,格式如下:
类名::成员
1. 重名数据成员
如果在派生类中定义了与基类相同名字的数据成员,根据继承规则,在建立派生类对象时,系统会分别建立不同的存储空间。
举例
例三!
2. 重名成员函数
在派生类中定义与基类同名的成员函数,称为在派生类中重载基类的成员函数。由调用形式指示this指针的不同类型,调用不同
版本的成员函数。
举例:
例四!
2.3派生类中访问静态成员
基类定义的静态成员,将被所有派生类共享(基类和派生类共享基类中的静态成员)。
根据静态成员自身的访问特性和派生类的继承方式,在类层次体系中具有不同的访问性质 。
派生类中访问静态成员,用以下形式显式说明:
类名 :: 成员
或通过对象访问 对象名.成员
3. 基类的初始化
在创建派生类对象时用指定参数调用基类的构造函数来初始化派生类继承基类的数据。
派生类构造函数声明为:
派生类构造函数(变元表) :基类(变元表) ,对象成1(变元表)…对象成员n (变元表);
构造函数执行顺序:基类,对象成员,派生类。
派生类构造函数和析构函数的使用原则:
1.基类的构造函数和析构函数不能被继承。
2.如果基类没有定义构造函数或有无参的构造函数, 派生类也可以不用定义构造函数。
3.如果基类无无参的构造函数,派生类必须定义构造函数。
4.如果派生类的基类也是派生类,则每个派生类只负责直接基类的构造。
5.派生类是否定义析构函数与所属的基类无关。
4.赋值兼容规则
赋值兼容规则指在程序中需要使用基类对象的任何地方,都可以用公有派生类的对象来替代。
- 赋值兼容规则中所指的替代包括以下的情况:
a 派生类的对象可以赋给基类对象。
b 派生类的对象可以初始化基类的引用。
c 派生类的对象的地址可以赋给基类类型的指针。
- 赋值兼容的可行性
1.通过公有继承,
a.派生类得到了除了构造、析构函数以外的所有成员.
b.且这些成员的访问控制属性也和基类完全相同。
c.便具备了基类的所有功能。
- 利用赋值兼容规则
a.派生类的对象可以赋给基类对象(强制类型转换)。
b.派生类的对象可以初始化基类的引用。
c.派生类的对象的地址可以赋给基类类型的指针。
例一:
#include<iostream>
using namespace std;
class Base//基类
{
private:
int b_numbr;
public:
Base(){}//基类不带参数的构造函数
Base(int i):b_number(i){}//基类带参数的构造函数
int get_number(){return b_number;}
void print(){cout<<b_number<<endl;}
};
class Derived:public Base//派生类
{
private:
int d_number;//派生类增加的数据成员
public:
Derived(int i,int j):Base(i),d_number(j){};//派生类构造函数
void print()//派生类增加的成员函数
{
cout<<get_number<<" ";//派生类继承的成员函数
cout<<d_number<<endl;
}
};
int main()
{
Base a(2);
Derived b(3,4);
cout<<"a is";
a.print();//基类的print
cout<<"b is";
b.print();//派生类的print
cout<<"base part of b is";
b.Base::print();//基类的print
return 0;
}
例二:
#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;
}
例三:
#include<iostream>
using namespace std;
class Base
{
public:
int a,b;
};
class Derived:public base
{
public :
int b,c;
};
int main()
{
Derived d ;
d.a=1;
d.base::b=2;
d.b=3;
d.c=4;
cout<<"d.a="<<d.a<<'\n'<<"d.Base::b="<<d.Base::b<<'\n'<<"d.b="<<d.b<<'\n'<<"d.c="<<d.c<<'\n';
}
程序运行结果:
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();
}
程序运行结果:
a1=0 a2=0
a1=0 a2=0
b1=1 b2=1