多态

一.动态绑定和静态绑定

(多态和重载)

静态绑定:编译器将所有事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

派生类中必须重定义基类纯虚函数,或继承纯虚函数。

派生类重定义所有基类纯虚函数才不是抽象类。

原文地址:https://www.cnblogs.com/wanderingzj/p/5295689.html