20200227

@ShiKaiWi

Plan

60min: CPP

Notes

类型转换

  • 任何一个对象都有两个类型,一个是静态类型,一个是动态类型,除非是指针或者引用,其他对象的这两个类型都是一致的,对于指针和引用,动态类型和静态类型不一样,然而可访问的域却由静态类型决定。
  • 派生类的指针或引用,可以转换成基类的指针或引用的原因在于派生类的内存分布中,包含了基类的部分,因此可以被合法的访问到,然而反过来却不行,因为基类的内存分布中不包含派生类的部分。
  • 对象之间不存在类型转换,实际存在的类似转换现象实际上是触发了赋值运算符或者拷贝构造函数,比如基类的拷贝构造函数可以接收一个派生类的对象引用,然而需要注意的时候,这时候的构造,只有派生类中的基类部分才会被拷贝。

虚函数

  • 虚函数必须要由定义。
  • 派生类的虚函数使用 override 标记是标准的行为,这个原因在于内部层级的作用域内的函数声明会直接基于名字覆盖掉外层作用域的声明(没有重载发生),也就是说如果不标记 override 的话,实际你很有可能因为写错了形参类型,而以为自己覆盖了某个基类的 virtual method,但是实际上是把所有的同名的 virtual method 都覆盖了。
  • 保持虚函数在基类和派生类中的默认实参的值一样,否则是很有可能和预期不符合。

纯虚函数

  • 相当于 java 中的接口。
  • virtual method 的最后(const 等限定符之后)增加 = 0
  • 含有 pure virtual method 的 class 是 abstract base class,纯虚基类,无法被实例化。

继承的访问权限

继承的权限比较复杂,我个人觉得很多太过精确,没有什么特别的需要,因此我总结了自己认为的最佳做法,可以让所有的访问权限都符合自然:

  • 使用 public 继承
  • 不要用 friend class

其他关于访问权限的知识:

  • 考类一个类的访问权限时,我们实际上实在回答三种"访问者",普通用户,类的实现者,派生类。
  • protected 是为派生类这个访问者准备的访问权限,即可以让派生类访问到基类的 protected 成员,又不会让外界访问到
  • 友元和类的关系无关于继承。

其他发现

  • 基类如果没有定义默认构造函数,则派生类需要显示的为基类进行构造

练习代码:

#include <iostream>

class Base {
public:
  int i;
  Base() = default;
  Base(int i) : i(i) {}
  virtual void double_price() {
    std::cout << "Base double price" << std::endl;
  };
};

class Derived : public Base {
public:
  int m;
  Derived(int i) : m(i) {}
  void double_price(int x) {
    std::cout << "Derived double price: x:" << x << std::endl;
  }
};

int main() {
  Derived D(10);
  std::cout << D.i << std::endl;
  D.Base::double_price();
  return 0;
}

More