一、基类和派生类
类继承关系的语法形式
class 派生类名:基类名表
{
数据成员和成员函数说明
};
其中,“基类名表”的语句格式如下:
访问控制 基类名1,访问控制 基类名2,…
“访问控制”是表示继承权限的关键字,称为访问描述符。可以是:
- public 公有继承
- private 私有继承
- protected 保护继承
二、访问控制
一个派生类公有继承一个基类时,基类中所有公有成员(由public定义的数据成员或成员函数)成为派生类的公有(public)成员,基类中所有保护成员(由protected定义的数据成员或成员函数)成为派生类的保护(protected)成员。
一个派生类私有继承一个基类时,基类中所有公有成员和保护成员同时成为派生类的私有(private)成员。
一个派生类保护继承一个基类时,基类中所有公有成员和保护成员同时成为派生类的保护(protected)成员。
- 不论派生类以何种方式继承基类,都不能直接使用基类的私有(private)成员。
1、公有继承
基类 | 派生类 |
private成员 | |
protected成员 | protected成员 |
public成员 | public成员 |
private成员 | |
protected成员 | |
public成员 |
using namespace std;
class A
{
public:
void getxy()
{
cin>>x>>y;
}
void putxy()
{
cout<<x<<" "<<y<<endl;
}
protected:
int x,y;
};
class B:public A
{
public:
int gets(){return s;}
void makes()
{
s=x*y;//使用基类数据成员x,y
}
protected:
int s;
};
class C:public B
{
public:
void geth()
{
cin>>h;
}
int getv(){return v;}
void makev()
{
makes();//使用基类成员函数
v=gets()*h;//使用基类成员函数
}
protected:
int h,v;
};
int main()
{
A a;B b;C c;
a.getxy();
a.putxy();
b.getxy();
b.makes();
cout<<b.gets()<<endl;
c.getxy();
c.geth();
c.makev();
cout<<c.getv()<<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;//base::b使用的是base类的数据成员b
d.b=3;//这里使用的是derived类的数据成员b
d.c=4;
}
2、重名成员函数
同理,访问基类重名成员函数时用指定类域的方法限定。
四、派生类中访问静态成员
- 基类定义的静态成员,将被所有派生类共享(基类和派生类共享基类中的静态成员)
- 根据静态成员自身的访问特性和派生类的继承方式,在类层次体系中具有不同的访问性质
- 派生类中访问静态成员,用以下形式显式说明:类名::成员 或通过对象访问 对象名.成员
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();//i=1
y.f();
cout<<"static i="<<B::i<<endl;//i=8
cout<<"static i="<<x.i<<endl;//i=8
//cout<<"static i="<<y.i<<endl;
}
五、基类的初始化
- 在创建派生类对象时用指定参数调用基类的构造函数来初始化派生类继承基类的数据
- 派生类构造函数声明为
- 构造函数执行顺序:基类→对象成员→派生类
using namespace std;
class base
{
public:
int x;
base(int i):x(i){}
};
class derived:public base
{
int a;
public:
derived(int j):a(j*10),base(2)
{
cout<<x<<" "<<a<<endl;
}
};
int main()
{
derived d(1);//程序运行结果:2 10
}
- 在撤消派生类对象时,析构函数的执行顺序:派生类的析构函数→对象成员的析构函数→基类的析构函数
六、多继承
- 一个类有多个直接基类的继承关系称为多继承
- 多继承声明语法
class 派生类名:访问控制 基类名1,访问控制 基类名2,…,访问控制 基类名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;
总结:继承就是在已有类基础上创建新类的过程。对于一些类共有的部分,使用继承可以减少代码量,提高效率。