温习C++的面向对象基础的笔记

Posted by Srefan on February 6, 2017

  • 面向对象三个基本特征: 封装, 继承, 多态.

封装

  • 对象和类概念的主要特征.
  • 客观的事物 封装成 抽象的类 ,类把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏.

继承

  • 继承是一种能力: 子类可以使用 父类的所有数据和方法 ,在无需重新编写父类的情况下对 所有数据和方法 进行扩展.
  • 子类 = 派生类
  • 父类 = 超类 = 基类

多重继承

  • C++ 允许 类 多重继承.
  • 相对应,一些面对对象的编程语言不支持 类多重继承, 模式是 类 单一继承 + 接口 多重继承.
  • 优点: 免去笨拙的混合继承的利处.
  • 缺点: 多处混淆的弊端.
  • 最大的问题是 钻石型继承结构 (DOD, Diamond of Death).
  • 解决办法: 虚继承. 使用虚拟继承后,当系统碰到多重继承时就会自动先加入一个基类的拷贝,当再次请求一个基类的拷贝时就会被忽略,保证继承类成员函数的唯一性.
    1. 当一个类有多个父类时,每个父类在内存中依次排列,然后该类自己的成员.
    2. 每一个父类的镜像中,都包含有独立的虚函数表.
    3. 当把子类的指针Upcast的时候,两种Upcast的方式得到的结果分别指向各自的父类镜像.
    4. 当两个父类重载的虚函数不同时,会使用Thunk机制,也就是说,虚函数表中的函数指针并不指向实际的虚函数,而是指向一小段代码.在这一小段代码中,会修改This指针(ECX寄存器),使之指向合适的父类镜像,然后再跳转到实际的虚函数体.
    5. 当不使用虚继承时,共同基类的成员对象,在子类中会有独立两分(从两个父类各自继承了一份).
    6. 当使用虚继承时,共同基类的成员对象也会在虚函数表中记录,访问它必须先查找虚函数表.

多态

  • 多态的方式: 重载(overload), 覆盖(override), 隐藏(hide).

重载 (overload)

  • 同一个类不同的方法 使用 相同的方法名 ,但 参数不同 .调用的时候根据函数的 参数 来区别不同的函数.
    1. 相同的范围 (同一个类中).
    2. 方法名字相同.
    3. 参数个数或者类型不同.
    4. virtual 关键词可有可无.
  • 执行情况: 看参数决定.

覆盖 (override)

  • 派生类中重新对基类中的虚函数重新实现.方法名和参数一样,方法的实现体不一样.
    1. 不同的范围 (分别位于派生类和基类中).
    2. 方法名字相同.
    3. 参数相同.
    4. 基类方法必须有 virtual 关键词.
  • 执行情况: 调用派生类.

隐藏 (hide)

  • 派生类的成员方法隐藏了基类函数的成员方法.
    1. 如果派生类的函数与基类的函数同名,但是参数不同,此时,不论有无virtual关键字,基类的函数将被隐藏(注意别与重载混淆).
    2. 如果派生类的函数与基类的函数同名,但是参数相同,但是基类函数没有virtual关键字.此时,基类的函数被隐藏(注意别与覆盖混淆).
  • 执行情况: 用什么就调用什么.

派生类和基类的方法名和参数都相同, 属于覆盖;
只是方法名相同, 参数并不相同, 则属于隐藏.

代码解析

// 基类的实现
class Base
{
public:
    virtual void f(float x)
    {
        cout << "Base::f(float) " << x << endl;
    }
    
    void g(float x)
    {
        cout << "Base::g(float) " << x << endl; 
    }
    
    void h(float x)
    {
        cout << "Base::h(float) " << x << endl;
    }
};

// 派生类的实现
class Derived: public Base
{
public:
    // Derived::f(float) 覆盖(override)了方法 Base::f(float)
    virtual void f(float x)
    {
        cout << "Derived::f(float) " << x << endl;
    }
    
    // Derived::g(int) 隐藏(hide)了方法 Base::g(float), 而不是重载(overload) -- 与 `隐藏-1` 的情况一致.
    void g(int x)
    {
        cout << "Derived::g(int) " << x << endl;
    }
    
    // Derived::h(float) 隐藏(hide)了 Base::h(float), 而不是覆盖(override) -- 与 `隐藏-2` 的情况一致.
    void h(float x)
    {
        cout << "Derived::h(float) " << x << endl;
    }
};

此文参考于 hit-alibaba.github.io,十分感谢.
所有引用内容版权归原作者所有.
使用 知识共享“署名-非商业性使用-相同方式共享 3.0 中国大陆”许可协议 授权.