多态
一.动态绑定和静态绑定
(多态和重载)
静态绑定:编译器将所有事s()调用绑定到s()的代码处
1 #include <iostream> 2 using namespace std; 3 4 void s() {} 5 6 int main() 7 { 8 s(); 9 return 0; 10 }
动态绑定:直到程序运行时,才将函数名绑定到其入口,每个类都有虚函数表,所有虚函数的地址
二.虚函数(构造函数不行,静态函数不行)
1 #include <iostream> 2 using namespace std; 3 4 class A 5 { 6 public: 7 virtual void s() // 子类中即使无virtual 也自动成为虚函数,最好在子类中都声明为virtual,virtual仅在声明时需要 8 { 9 cout << "A" << endl; 10 } 11 }; 12 13 class B: public A 14 { 15 public: 16 virtual void s() 17 { 18 cout << "B" << endl; 19 } 20 }; 21 22 23 class C: public A 24 { 25 public: 26 virtual void s() 27 { 28 cout << "C" << endl; 29 } 30 }; 31 32 33 int main() 34 { 35 A *a; 36 a = new A; 37 a->s();//A 38 a = new B; 39 a->s();//B 40 a = new C; 41 a->s();//C 42 43 B b; 44 A &a1 = b; 45 a1.s();//B 46 return 0; 47 }
在非构造函数,非析构函数中调用虚函数,是多态
在构造,析构函数中调用虚函数不是多态,编译时即可确定
1 #include <iostream> 2 using namespace std; 3 4 class A 5 { 6 public: 7 virtual void act1() 8 { 9 cout << "A::act1()" << endl; 10 } 11 void act2() 12 { 13 act1(); 14 } 15 }; 16 17 class B: public A 18 { 19 public: 20 virtual void act1() 21 { 22 cout << "B::act1()" << endl; 23 } 24 }; 25 26 int main() 27 { 28 B b; 29 b.act2(); // "B::act1()" 30 return 0; 31 }
1.B是A的派生类,act1()是类A中的虚函数,B实际上调用A::act2(),在A::act2()中调用act1(),由于act1()是虚函数,产生动态联编,根据运行时情况选择了B::act1()
void act2(A *this) {this -> act1()};很明显是多态。
2.把A::act1()改为非虚函数,结果为A::act1(),静态联编。
3. A::act2()改为void act2(){A::act1()}结果为A::act1()。由于加入了成员名限定,因此也是静态联编。
三.
1.C++使用vtable实现虚成员函数运行时绑定,vtable支持运行时查询,把函数名绑定到vtable中特定入口。vtable需要额外空间,对vtable进行查询也需要额外时间。
2.构造函数不可用是虚函数,但析构可以是。
1 #include <iostream> 2 using namespace std; 3 4 class A 5 { 6 public: 7 A() 8 { 9 cout << "A" << endl; 10 p = new char[500]; 11 } 12 ~A() 13 { 14 cout << "~A" << endl; 15 delete[] p; 16 } 17 private: 18 char *p; 19 }; 20 21 class B: public A 22 { 23 public: 24 B() 25 { 26 cout << "B" << endl; 27 q = new char[500]; 28 } 29 ~B() 30 { 31 cout << "~B" << endl; 32 delete[] q; 33 } 34 private: 35 char *q; 36 }; 37 38 void f() 39 { 40 A *p = new B(); 41 delete p; 42 } 43 44 int main() 45 { 46 f();//因为析构函数不是virtual,所以p的数据类型A*仅调用~A(),而没有调用~B(),没有释放B()中分配的字节。 47 /* 48 A 49 B 50 ~A 51 */ 52 return 0; 53 }
1 #include <iostream> 2 using namespace std; 3 4 class A 5 { 6 public: 7 A() 8 { 9 cout << "A" << endl; 10 p = new char[500]; 11 } 12 virtual ~A() 13 { 14 cout << "~A" << endl; 15 delete[] p; 16 } 17 private: 18 char *p; 19 }; 20 21 class B: public A 22 { 23 public: 24 B() 25 { 26 cout << "B" << endl; 27 q = new char[500]; 28 } 29 ~B() 30 { 31 cout << "~B" << endl; 32 delete[] q; 33 } 34 private: 35 char *q; 36 }; 37 38 void f() 39 { 40 A *p = new B(); 41 delete p; 42 } 43 44 int main() 45 { 46 f(); 47 /* 48 A 49 B 50 ~B 51 ~A 52 */ 53 return 0; 54 }
四.重写和重载
重写函数签名完全相同,重载参数列表不同。
函数遮蔽:函数名相同,签名无所谓,虚函数同名,不同参也可遮蔽
1 #include <iostream> 2 using namespace std; 3 4 class A 5 { 6 public: 7 virtual void m(int x){} 8 }; 9 10 class B: public A 11 { 12 public: 13 void m(){} 14 }; 15 16 17 int main() 18 { 19 A *p = new B(); 20 B b; 21 p->m();//error,遮蔽 22 p->m(10); 23 b.A::m(3); 24 b.m(); 25 b.m(3);//error,遮蔽 26 return 0; 27 }
五.抽象基类
纯虚函数(在派生类重新定义之前不能调用),virtual 函数=0,类中至少有一个纯虚函数,这个类是抽象类,不能被实例化。
A a;//error
A *a, &a;//ok
派生类中必须重定义基类纯虚函数,或继承纯虚函数。
派生类重定义所有基类纯虚函数才不是抽象类。