1、继承和派生的概念
继承:在定义一个新的类B时,如果该类与某个已有的类A相似(指的是B拥有A的全部特点),那么就可以把A作为一个基类,而把B作为基类的一个 派生类( 也称子类)
- 派生类是通过对基类进行修改和扩充得到的。在派生类中,可以扩充新的成员变量和成员函数。
- 派生类一经定义后,可以独立使用,不依赖于基类。
- 派生类拥有基类的全部成员函数和成员变量,不论是private、protected、public 。
- 在派生类的各个成员函数中,不能访问基类中的private成员。
2、派生类对象的内存空间
派生类对象的体积,等于基类对象的体积,再加上派生类对象自己的成员变量的体积。在派生类对象中,包含着基类对象,而且基类对象的存储位置位于派生类对象新增的成员变量之前。
3、继承关系和复合关系
(1)继承:“是”关系。
- 基类A,B是基类A的派生类。
- 逻辑上要求:“一个B对象也是一个A对象”。
(2)复合:“有”关系。
- 类C中“有”成员变量k,k是类D的对象,则C和D是复合关系
- 一般逻辑上要求:“D对象是C对象的固有属性或组成部分”。
4、覆盖
派生类可以定义一个和基类成员同名的成员,这叫覆盖。
在派生类中访问这类成员时,缺省的情况是访问派生类中定义的成员。要在派生类中访问由基类定义的同名成员时,要使用作用域符号::。
5、存取权限说明符
(1)基类的private成员:可以被下列函数访问
- – 基类的成员函数
- – 基类的友员函数
(2)基类的public成员:可以被下列函数访问
- – 基类的成员函数
- – 基类的友员函数
- – 派生类的成员函数
- – 派生类的友员函数
- – 其他的函数
(3)基类的protected成员:可以被下列函数访问
- – 基类的成员函数
- – 基类的友员函数
- – 派生类的成员函数可以访问当前对象的基类的保护成员
class Father {
private: int nPrivate; // 私有成员
public: int nPublic; // 公有成员
protected: int nProtected; // 保护成员
};
class Son :public Father{
void AccessFather () {
nPublic = 1; // ok;
nPrivate = 1; // wrong
nProtected = 1; // OK ,访问从基类继承的protected 成员
Son f;
f.nProtected = 1; //wrong,f不是当前对象
}
}
6、派生类的构造函数
(1)在创建派生类的对象时,需要调用基类的构造函数:初始化派生类对象中从基类继承的成员。在执行一个派生类的构造函数之前,总是先执行基类的构造函数。
(2) 调用基类构造函数的两种方式
- – 显式方式:在派生类的构造函数中,为基类的构造函数提供参数. derived::derived(arg_derived-list):base(arg_base-list)
- – 隐式方式:在派生类的构造函数中,省略基类构造函数时,派生类的构造函数则自动调用基类的默认构造函数.
(3)派生类的析构函数被执行时,执行完派生类的析构函数后,自动调用基类的析构函数。
class Bug {
private :
int nLegs; int nColor;
public:
int nType;
Bug ( int legs, int color);
void PrintBug (){ };
};
class FlyBug: public Bug { // FlyBug 是Bug 的派生类
int nWings;
public:
FlyBug( int legs,int color, int wings);
};
// 派生类的构造函数
Bug::Bug( int legs, int color){
nLegs = legs;
nColor = color;
}
// 错误的FlyBug 构造函数
FlyBug::FlyBug ( int legs,int color, int wings){
nLegs = legs; // 不能访问
nColor = color; // 不能访问
nType = 1; // ok
nWings = wings;
}
// 正确的FlyBug 构造函数:
FlyBug::FlyBug ( int legs, int color, int wings):Bug( legs, color){
nWings = wings;
}
(4)封闭派生类对象的构造函数执行顺序
- 先执行基类的构造函数,用以初始化派生类对象中从基类继承的成员;
- 再执行成员对象类的构造函数,用以初始化派生类对象中成员对象。
- 最后执行派生类自己的构造函数
(5)封闭派生类对象消亡时析构函数的执行顺序
- 先执行派生类自己的析构函数
- 再依次执行各成员对象类的析构函数
- 最后执行基类的析构函数
7、public继承的赋值兼容规则
class base { };
class derived : public base { };
base b;
derived d;
(1)派生类的对象可以赋值给基类对象
b = d;
(2)派生类对象可以初始化基类引用
base & br = d;
(3)派生类对象的地址可以赋值给基类指针
base * pb = & d;
注:
- 如果派生方式是 private或protected,则上述三条不可行。
- protected继承时,基类的public成员和protected成员成为派生类的protected成员。
- private继承时,基类的public成员成为派生类的private成员,基类的protected成员成为派生类的不可访问成员。
- protected和private继承不是“是”的关系。
8、基类与派生类的指针强制转换
(1)公有派生的情况下,派生类对象的指针可以直接赋值给基类指针
Base * ptrBase = &objDerived;
ptrBase指向的是一个Derived类的对象;*ptrBase可以看作一个Base类的对象,访问它的public成员直接通过ptrBase即可
,但不能通过ptrBase访问objDerived对象中属于Derived类而不属于Base类的成员
(2) 即便基类指针指向的是一个派生类的对象,也不能通过基类指针访问基类没有,而派生类中有的成员。
(3)通过强制指针类型转换,可以把ptrBase转换成Derived类的指针
Base * ptrBase = &objDerived;
Derived *ptrDerived = (Derived * ) ptrBase;
程序员要保证ptrBase指向的是一个Derived类的对象,否则很容易会出错。
9、直接基类与间接基类
baseA->DerivedB->C->....
在声明派生类时,只需要列出它的直接基类
– 派生类沿着类的层次自动向上继承它的间接基类
– 派生类的成员包括
• 派生类自己定义的成员
• 直接基类中的所有成员
• 所有间接基类的全部成员
#include <iostream>
using namespace std;
class Base {
public:
int n;
Base(int i):n(i) {
cout << "Base " << n << " constructed"<< endl;
}
~Base() {
cout << "Base " << n << " destructed"<< endl;
}
};
class Derived:public Base{
public:
Derived(int i):Base(i) {
cout << "Derived constructed" << endl;
}
~Derived() {
cout << "Derived destructed" << endl;
}
};
class MoreDerived:public Derived {
public:
MoreDerived():Derived(4) {
cout << "More Derived constructed" << endl;
}
~MoreDerived() {
cout << "More Derived destructed" << endl;
}
};
int main(){
MoreDerived Obj;
return 0;
}
输出结果:
Base 4 constructed
Derived constructed
More Derived constructed
More Derived destructed
Derived destructed
Base 4 destructed