基本概念
子类具有基类的全部成员函数和成员变量,不论是 private,protected,public。
但子类的成员函数中 不能访问基类的 private成员。
class 子类名:public 基类名{
};
子类对象的体积,等于基类对象的体积 + 子类对象自己的
成员变量的体积。
子类对象中,包含着基类对象,且基类对象的存储位置位于 子类对象自己的
成员变量之前。
前向声明
可以声明一个类而不定义它。在声明之后,定义之前,该类 是一个不完全类型( incomplete type )。不完全类型只能以有限方式使用,不能定义该类型的对象,只能用于定义指向该类型的指针及引用,或者用于声明( 不是定义 )使用该类型作为形参类型或返回类型的函数。
class Dog; // Dog 类的声明
class Owner{
Dog dogs[10]; // [Error] field 'dogs' has incomplete type 'Dog [10]'
Dog * dogs[10]; // OK
void func(Dog m); // OK 这是一个声明
void func(Dog m){ // [Error] 'm' has incomplete type 这是一个定义
};
};
class Dog{
Owner m;
};
类与类之间的关系
- 继承(Inheritance)
- 复合(Composition)
继承关系:
一个子类的对象也是一个基类的对象,比如从学生类 派生出 中学生类,一个中学生同时也是一个学生。
复合关系:
类B 中有一个成员,是 类 A的对象,则 B和A是复合关系,一般要求 A对象 是 B对象的固有属性或组成部分。
Composition:构造由内而外,析构由外而内。
虚函数
成员函数进行声明时,前面有 virtual
关键字。
构造函数 和 static成员函数 不能是 虚函数。
多态
Son obj_son;
Father *p=&obj_son; 子类指针可以赋给基类指针
基类指针
调用 基类和子类中的同名虚函数时:
调用哪个类里的 虚函数 取决于 该指针 指向 哪个类的对象。
Son obj_son;
Father &r=obj_son; 子类对象可以赋给基类引用
基类引用
调用 基类和子类中的同名虚函数时:
调用哪个类里的虚函数 取决于 该引用 引用的是 哪个类的对象。
基类指针数组 存放指向各种子类对象的指针,遍历该数组,就能对各个子类对象 做各种操作,是很常用的做法。
多态 的关键在于 通过 基类指针 基类引用 调用一个 虚函数时,编译时不确定 到底调用的是哪个类的 函数,运行时才确定。
虚析构函数
通过 基类指针 删除子类对象时,通常只调用了基类的析构函数。
将 基类的析构函数 声明为 virtual,这样在进行删除时,就会先调用子类 的析构函数,然后再调用 基类的析构函数。
抽象类
包含纯虚函数的类。
抽象类 只能作为基类来使用, 不能创建 独立的抽象类的对象。
class A{
int a;
public:
virtual void Print() = 0; // 纯虚函数
void fun(){
cout<<"fun";
}
};
A obj; //[Error] cannot declare variable 'obj' to be of abstract type 'A'
A *a; // OK
a= new A; //[Error] invalid new-expression of abstract class type 'A'