五、继承、派生、复合

继承、派生、复合

一、继承和派生

1.1 基本概念

继承:在定义一个新的类B时,如果该类与某个已有的类A相似(指的是B拥有A的全部特点),那么就可以把A作为一个基类,而把B作为基类的一个派生类(也称子类)。

派生类是通过对基类进行修改和扩充得到的。在派生类中,可以扩充新的成员变量和成员函数。派生类一经定义后,可以独立使用,不依赖于基类。

派生类拥有基类的全部成员函数和成员变量,不论是private、 protected、 public 。但在派生类的各个成员函数中,不能访问基类中的private成员

1.2 派生类的写法

class 派生类名: public 基类名
{

};

1.3 派生类对象的内存空间

派生类对象的体积,等于基类对象的体积,再加上派生类对象自己的成员变量的体积。 在派生类对象中,包含着基类对象,而且基类对象的存储位置位于派生类对象新增的成员变量之前。

二、继承关系和复合关系

2.1 基本概念

继承:“ 是”关系。
- 基类 A, B是基类A的派生类。
- 逻辑上要求:“一个B对象也是一个A对象”。

复合:“ 有”关系。
- 类C中“有” 成员变量k, k是类D的对象,则C和D是复合关系
- 一般逻辑上要求:“ D对象是C对象的固有属性或组成部分”。

2.2 复合关系的使用

假如定义一个Master类,一个Dog类,Master最多有10只狗,狗最多有1个主人,如果像下面这样写:

class Dog;
class Master
{
    Dog dogs[10];
};
class Dog
{
    Master m;
};

错误!循环定义了,正确定义方法如下:

class Master; //Master必须提前声明
class Dog {
    Master * pm;
};
class Master {
    Dog * dogs[10];
};

三、可访问范围说明符

3.1 基本概念

基类的private成员: 可以被下列函数访问
• 基类的成员函数
• 基类的友员函数

基类的public成员: 可以被下列函数访问
• 基类的成员函数
• 基类的友员函数
• 派生类的成员函数
• 派生类的友员函数
• 其他的函数

基类的protected成员: 可以被下列函数访问
• 基类的成员函数
• 基类的友员函数
• 派生类的成员函数可以访问当前对象的基类的保护成员

小结一下,private只有自己和朋友能访问;protected只有自己、朋友和儿子能访问;public都可以被访问。

3.2 程序示例

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不是当前对象,即不是this
    }
};

int main(){
    Father f;
    Son s;
    f.nPublic = 1; // Ok
    s.nPublic = 1; // Ok
    f.nProtected = 1; // error
    f.nPrivate = 1; // error
    s.nProtected = 1; //error
    s.nPrivate = 1; // error

    return 0;
}

四、派生类的构造函数

4.1 派生类构造函数

派生类对象包含基类对象,执行派生类构造函数之前,先执行基类的构造函数。
在派生类的构造方法需要交代基类对象初始化的方法,具体形式:

构造函数名(形参表): 基类名(基类构造函数实参表)
{ }

比如:

class Bug {
    private :
        int nLegs; int nColor;
    public:
        Bug (int l, int c): nLegs(l), nColor(c) { }
};

class FlyBug: public Bug { 
    int nWings;
    public:
        //正确的FlyBug构造函数:
        FlyBug(int legs, int color, int wings):Bug(legs, color) { nWings = wings; }
};

4.2 构造函数和析构函数的调用顺序

派生类的构造函数的调用顺序:
1. 调用 基类 的构造函数。初始化派生类对象中从基类继承的成员。
2. 调用成员对象类的构造函数。初始化派生类对象中成员对象。
3. 调用自身的构造函数。

派生类析构函数的调用顺序
1. 调用自身的析构函数
2. 调用成员对象类 的析构函数
3. 调用基类的析构函数

总之,无论是成员对象还是基类对象,构造函数都是从里到外开始执行的,析构函数都是从外到里开始执行的。就像生活中做一台电脑和拆一台电脑一样。

五、public继承的赋值兼容原则

其实很简单,我们可以说猫是动物,却不能说动物是猫,就这样。

class base { };
class derived : public base { };
base b;
derived d;
  1. 派生类的对象可以赋值给基类对象
    b = d;
  2. 派生类对象可以初始化基类引用
    base & br = d;
  3. 派生类对象的地址可以赋值给基类指针
    base * pb = & d;

如果派生方式是 private或protected,则上述三条不可行。

你可能感兴趣的:(C++,继承,复合,派生,可访问范围)