组合与继承
一.组合
定义:将一个类另一个类的成员。
初始化:
1.成员对象初始化使用初始化列表语法,如果没有进行初始化,而且成员对象所属的类不存在构造函数,就会引起错误。
- 如果类中有多个对象成员,在初始化列表用逗号隔。
- 为了不引起困扰,初始化成员顺序的顺序与声明顺序一致。
- 当组合对象被撤销时,先执行组合对象的析构函数,在执行成员对象的析构函数。
二.复用类
包装:在一个新的类中封装一个已有的类
class XCircle
{
public:
XCircle(); //构造函数
void Xdraw(); //绘制圆形
double calc_area(); //计算面积
double calc_perimeter(); //计算周长
void zoom(double factor); //按比例缩放
//…
}
封装到新类
class Circle
{
public:
Circle() : xc() {}
void draw() { xc.Xdraw(); }
double area() { return xc.calc_area(); }
double perimeter() { return xc.calc_perimeter(); }
void scale(double factor) { xc.zoom(factor); }
private:
XCircle xc;
};
三.继承
1.继承是面对对象的核心特征之一,也是复用已知类的一种。
被继承的已有类叫做基类,继承后得到的新类叫做派生类。派生类也可以被继承,这样形成了继承层次。
继承语法:
class 派生类名字: 访问限定符 基类名字
{
成员声明;
};
定义派生类时可以指定访问限定符public,protected,private。如果不指定,
对于 class类默认为private(私有继承);对于struct 类默认为public(公有继承)。
不论种方式继承基类,派生类都不能直接使用基类的私有成员 。
改变个别成员的访问性,可以使用using声明,
语法 class A:(public|private|protected)Base
{
public(private|protected):
using Base::成员名;//将基类的成员声明为public(private或protected)
//...
};
不能改变基类private成员的访问权限;
- 基类初始化
一.派生类构造函数声明
派生类构造函数 ( 变元表 ) : 基类 ( 变元表 ) , 对象成员1( 变元表 )… 对象成员n ( 变元表 ) ;
class derived_class : private parent_class
{
int data3 ;
parent_class data4 ;
public:
derived_class ( int p1 , int p2 , int p3 , int p4 , int p5 ): parent_class ( p1 , p2 ) , data4 ( p3 , p4 )
{
data3 = p5 ;
}
};
构造函数执行顺序:基类 -->对象成员-->派生类
- 派生类的创建
一般步骤:
一.吸收基类成员(全部吸收(构造、析构除外),但不一定可见)
在C++的继承机制中,派生类吸收基类中除构造函数和析构函数之外的全部成员。
二.改造基类成员
通过在派生类中定义同名成员(包括成员函数和数据成员)来屏蔽(隐藏)在派生类中不起作用的部分基类成员,原来基类中的内容在派生类中不起作用。
- 添加派生类新成员
需要在派生类中添加新成员,以保证派生类自身特殊属性和行为的实现。
派生类构造函数初始化
一.派生类构造函数的一般格式为:
派生类::派生类名(参数总表):基类名(参数表)
{
// 派生类新增成员的初始化语句
}
二.注意事项:
如果基类没有定义构造函数或有无参的构造函数, 派生类也可以不用定义构造函数
如果基类无无参的构造函数,派生类必须定义构造函数
如果派生类的基类也是派生类,则每个派生类只负责直接基类的构造
派生类是否定义析构函数与所属的基类无关
- 特殊
在类后加上final,防止该类被继承。
基类的构造函数和析构函数不能被继承
继承的赋值兼容规则
赋值兼容规则指在程序中需要使用基类对象的任何地方,都可以用公有派生类的对象来替代。
几种方式
a 派生类的对象可以赋给基类对象
b 派生类的对象可以初始化基类的引用
c 派生类的对象的地址可以赋给基类类型的指针
在替代之后,派生类对象就可以作为基类的对象使用,但只能使用从基类继承的成员。
注意问题:
声明为指向基类的指针可以指向它的公有派生类的对象,但不允许指向它的私有派生类的对象。
四.多态
虚函数声明
基类在成员函数声明之前加上virtual关键字使得该函数执行进行动态捆绑。
语法:
Virtual 返回类型 成员函数名(参数列表);
Virtual只能在类内的成员函数之前使用,不能再类外使用。
派生类可以重新定义基类的虚函数,这种是覆盖。
虚函数覆盖规则
在派生类中覆盖基类的虚函数时要用相同的参数列表和返回类型;
若 派生类中重新定义的基类的虚函数保持参数名相同和参数列表相同,但仅仅返回类型不同,会报错;
实现多态的步骤
- 在基类中将需要的多态调用的函数声明为virtual;
- 在派生类中覆盖基类的虚函数,实现各自所需的功能;
- 用基类的指针或引用实际指向派生类对象,通过基类指针或引用调用虚函数;、
如果派生类没有覆盖,那么会调用基类的虚函数。
纯虚函数与抽象类
抽象类作用:描述一般性的通用概念;为一组派生类提供公共接口;
纯虚函数
语法:
Virtual 返回类型 函数名 (参数表)=0;
注意:
如果一个类 包含至少一个纯虚函数,这个类就是抽象类;
如果一个抽象类中的所有函数都是纯虚函数,这个类就是纯抽象类;
如果要继承一个抽象类,派生类中要将所有虚函数都覆盖,否则这个派生类也是抽象类;
不能创建抽象类的实例,但可以创建由抽象类派生出来的具体子类的实例;