Lecture 6

Polymorphism


动态绑定 & 静态绑定

对于这样一段代码,move() 是静态绑定,在运行时调用基类下的 move() 函数;render() 是动态绑定,所以即使 foo() 函数传入的指针是 shape() 类型,运行时依然调用子类下的 render() 函数
notion image
有了这种特性,我们就可以不加区分的将不同子类的实例存在同一个以基类指针定义的容器中,程序运行时会通过动态绑定自动调用子类中的函数,例如
同理,析构函数也可以动态绑定,例如
输出结果为
notion image

Virtual


对于这样一段代码,输出为
notion image
如果加一个 virtual 函数 bar()
b 开辟的空间会增加到 16,存储结构如下:
8 bits
vptr
4 bits
data = 10
4 bits
内存对齐
这个 vptr 指向一张表,用于存放 virtual member functions 的地址。如果以基类直接实例化对象,对象的 vptr 将指向基类的 virtual member functions;如果以派生类实例化对象,对象的 vptr 将指向子类中多态的 member functions
notion image
notion image

Conversions


有三种赋值方式:
  • D => B
    • e.g.
      CircleEllipse 的派生类,会发生数据的截断 ( slice of ),只把派生类中属于基类的数据赋值给基类对象,而不管属于派生类自己的数据
      派生类的 vptr 会直接被忽略,仍然使用基类的 vptr
  • D* => B*
    • e.g.
      这就是多态的性质,但直接这样写会造成内存泄露
  • D& => B&
    • e.g.
      和 pointer 效果一样
       
虽然上面的示例没有用 overide ,但规范的做法应该是把 overide 加上,这样可以让编译器更严格地检查
例如,若基类的构造函数和子类的构造函数对同一变量有不同的初始化,而没有用 overide 严格规定关系,会导致子类构造函数变成一个普通函数,而初始化仍用基类的初始化
 
Loading...
文章列表

没有找到文章