派生 | 公有继承 | 包含 | 私有继承 | 保护继承 | 多重继承 |
关系 | is-a | has-a | has-a |
hsa-a |
is-a |
关键词 | public | private | protected | ||
继承内容 |
继承了基类的数据成员(实现)和基类的方法(接口),但基类的构造函数和析构函数不被继承 |
将对象作为一个命名的成员对象添加到类中,获得了成员对象的实现,但没有继承接口 | 将对象作为一个未被命名的继承对象添加到类中,基类的公有成员和保护成员都将成为派生类的私有成员,类方法将不会成为派生类对象共有接口的一部分,但可以在派生类的成员函数中使用它们 | 基类的公有成员和保护成员都将成为派生类的保护成员 |
多重继承会得到从基类公有派生出的多个类的多组基类数据成员,而使用虚基类,会使得从多个类派生出的对象,只继承一个基类对象 |
添加内容 | 构造函数、析构函数、额外的数据成员和成员函数 | 创建一个包含其他对象的类,将数据成员声明为私有的,添加构造函数、析构函数和接口 | |||
访问权限 |
不能直接访问基类的私有成员,必须通过基类方法进行访问,但派生类能够访问基类的保护成员(包括数据成员或成员函数) |
被包含对象的接口不是公有的,但可以在类方法中使用它 ,即使用对象名来调用方法,但由于包含不是继承,因此不能访问基类保护成员,且不能重新定义虚函数 |
获得实现,但不获得接口, 使用类名和作用域解析运算符来调用方法,能访问基类保护成员,可以重新定义虚函数,重新定义的函数将只能在类中使用,而不是公有的,第三代类不能使用基类的接口,这是因为基类的公有方法在派生类中将变成私有方法 |
基类的公有方法在第二代中将变成受保护的,在第三代派生类可以使用它们 | |
构造函数 |
使用成员初始化列表,调用基类构造函数 使用成员初始化列表,调用基类复制构造函数 县调用基类构造函数再调用派生类构造函数 |
构造函数初始化成员对象,而不是继承的对象,所以在初始化列表中使用的是成员名,而不是类名,初始化列表中的每一项都调用与之匹配的构造函数 | 使用成员初始化列表语法,使用类名而不是成员名来标识构造函数 | 对于虚基类,构造函数无法自动传递信息,需要显式调用所需基类的构造函数 | |
析构函数 | 先调用派生类析构函数,再调用基类析构函数,基类析构函数通常声明为虚析构函数 | ||||
指针/引用 |
基类指针可以在不进行显式转换的情况下,引用派生类对象,但不可以将基类对象和地址赋给派生类引用和指针 |
使用强制类型转换,以访问内部私有继承的对象或友元函数,通常是强制转换为const引用 | |||
多态 | 在派生类中重新定义基类的方法,通常应该将基类方法声明为虚的,这样,程序将根据对象类型而不是引用或指针的类型来选择方法版本 |