C++ 面向对象笔记

C++面向对象

  1. 封装:实现与接口分离
  2. 继承:定义相似的类型,并对这种相似关系建模。
  3. 多态(动态绑定):一定程度上忽略相似类型的差别,用一种统一的方式使用这些对象,用虚函数实现。
    • 动态绑定什么时候发生:用父类的指针或者引用调用虚函数时,根据其绑定的对象类型决定运行时的函数版本(虚函数)。注意,虚函数必须有相同的形参列表和返回类型

静态类型和动态类型

静态类型就是字面类型,动态类型是指针或引用所指内存中对象的实际类型。如果表达式不是指针或引用,那么静态类型永远等于动态类型。当通过指针或引用调用类的虚函数时,会根据动态类型去调用对应版本的虚函数。否则根据静态类型调用对应的函数。

重载、重写(覆盖)、隐藏

面向过程:

重载函数:函数名一样,形参列表或者返回类型不一样

面向对象:

重写(覆盖):虚函数

隐藏:非虚函数,但函数名相同,形参列表和返回值不要求,父类的函数会被隐藏。子类要想调用父类被隐藏的函数,需要用作用域描述符。

继承中的类作用域

子类作用域嵌套在父类作用域里。这也就是为什么父类指针没法调用子类的非虚函数,及时这个指针指向的对象是子类对象,当前作用域查不到的名字会向外查找。

成员查找过程

对于一次p->memp->mem_fun()obj.memobj.mem_fun()

  1. 首先确认p的静态类型,根据静态类型确定其作用域。
  2. 从当前作用域向外找,直到找到p->mem。否则编译报错。
  3. 进行常规函数调用检查,注意名字查找先于类型检查,如果函数的类型检查错误,则不会继续向外查找符合类型的其他函数。
  4. 类型检查合法,下面分情况:
    1. 是虚函数且通过指针或引用调用:根据动态类型确定虚函数版本
    2. 不是虚函数,或者通过对象调用:常规成员函数调用。

类型转换

类之间只有指针和引用存在类型转换,对象之间不存在类型转换。对象赋值或者初始化的时候实际上是调用某个函数,而函数的参数往往是一个引用,这就造成了对象之间类型转换的假象。

Base a;
Derived b(a); //Base(const Base&)
b = a; //Base& operator=(const Base&)

虚函数

父类有两种函数,一种是希望子类直接继承的原封不变的函数。一类是子类需要实现为自己版本的函数,类型相关的函数,也是虚函数。任何构造函数之外非静态函数都可以是虚函数。

声明

父类的虚函数前边声明virtual,实现不用。该函数在子类中隐式也为虚函数。子类对虚函数的声明最好在结尾加上override来说明要覆盖基类的虚函数。

纯虚函数

当一个类的虚函数只是用来继承,而对类本身没有作用的话,可以声明为虚函数,在声明后边加上=0。这种类成为抽象类。

析构函数要声明为虚函数

当指针静态类型为父类指针时,而动态类型为子类,delete会选择正确的析构函数。

Tips:

  • virtual只出现在声明,不出现在定义。

  • 派生类可以访问publicprotected成员,不可访问private成员。

  • 派生类向基类的类型转换只对指针和引用有效,对象之间不存在类型转换。

  • 基类的引用或指针的静态类型和动态类型可能不一样。

  • 当通过基类的引用或者指针调用虚函数时,在运行时才确定下来调用的虚函数的版本,如果指针或引用绑定的是派生类的对象,那会运行派生类的虚函数。当通过对象调用虚函数时,在编译期就可以确定。

  • 继承虚函数最好在声明结尾写上override

  • 虚函数的默认实参按调用的静态类型的默认实参决定。

  • 避免对虚函数动态绑定:使用作用域运算符:DerivedP->Base::f();。通常情况下是派生类调用其基类的虚函数版本时需要使用,基类版本通常完成所有继承层次中类型的共同任务,而派生类中的虚函数只需定义和自身密切相关的操作。

  • 在Quote的继承体系中增加Disc_quote类是重构,重构负责将操作或数据从一个类转移到另一个类,即使重构了继承体系,引用了Bulk_quote或Quote的代码也无需改变。

  • 派生类的类作用域嵌套在基类的作用域里,重名会将基类中的成员隐藏,而不会重载即使是参数不一样。当然可以使用作用域运算符访问被覆盖的成员Base::covered_member

  • 如果想重载基类中的函数,需要在派生类中using Base::f;,然后再定义派生类的f()

  • 析构函数要定义成虚函数,防止一个Base类指针指向派生类,delete的时候选错析构函数的版本。

  • 向一个基类的容器传入派生类对象,只会将其基类的部分传入,调用虚函数的版本是基类的版本。好的做法是定义成基类的智能指针的容器,传入容器时派生类的指针会转换成基类的指针,但他指向的还是派生类对象,指针具有动态类型,调用虚函数会调用派生类版本。

  • delctype(f)获得函数类型,用decltype(f)*获得函数指针

    multiset<int, decltype(com)*> st(com);
    
posted @ 2022-07-02 10:16  hellozhangjz  阅读(55)  评论(0)    收藏  举报