1. 静态绑定发生在编译期,动态绑定发生在运行期;
  2. 对象的动态类型可以更改,但是静态类型无法更改;
  3. 要想实现动态,必须使用动态绑定;
  4. 在继承体系中只有虚函数使用的是动态绑定,其他的全部是静态绑定;

建议:
绝对不要重新定义继承而来的非虚(non-virtual)函数(《Effective C++ 第三版》条款36),因为这样导致函数调用由对象声明时的静态类型确定了,而和对象本身脱离了关系,没有多态,也这将给程序留下不可预知的隐患和莫名其妙的BUG;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class A {
public:
// virtual
void func() { std::cout << "A::func()\n"; }
};

class B : public A {
public:
void func() { std::cout << "B::func()\n"; }
};

class C : public A {
public:
void func() { std::cout << "C::func()\n"; }
};

int main() {
C *pc = new C(); //pc的静态类型是它声明的类型C*,动态类型也是C*;
B *pb = new B(); //pb的静态类型和动态类型也都是B*;
A *pa = pc; //pa的静态类型是它声明的类型A*,动态类型是pa所指向的对象pc的类型C*;
pa = pb; //pa的动态类型可以更改,现在它的动态类型是B*,但其静态类型仍是声明时候的A*;
C *pnull = nullptr; //pnull的静态类型是它声明的类型C*,没有动态类型,因为它指向了NULL;
pa->func(); //A::func() pa的静态类型永远都是A*,不管其指向的是哪个子类,都是直接调用A::func();
pc->func(); //C::func() pc的动、静态类型都是C*,因此调用C::func();
pnull->func(); //C::func() 不用奇怪为什么空指针也可以调用函数,因为这在编译期就确定了,和指针空不空没关系;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class A{
public:
virtual void func(){ std::cout << "A::func()\n"; }
};
class B : public A {
public:
void func() { std::cout << "B::func()\n"; }
};

class C : public A {
public:
void func() { std::cout << "C::func()\n"; }
};
C *pc = new C();
B *pb = new B();
A *pa = pc;
pa = pb;
C *pnull = nullptr; //pnull的静态类型是它声明的类型C*,没有动态类型,因为它指向了NULL;
pa->func(); //B::func() 因为有了virtual虚函数特性,pa的动态类型指向B*,因此先在B中查找,找到后直接调用;
pc->func(); //C::func() pc的动、静态类型都是C*,因此也是先在C中查找;
pnull->func(); //空指针异常,因为是func是virtual函数,因此对func的调用只能等到运行期才能确定,然后才发现pnull是空指针;