• 多态


    一.动态绑定和静态绑定

    (多态和重载)

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

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

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

  • 相关阅读:
    React Virtual Dom 与 Diff
    打造前端CI/CD工作流
    webpack-chain明细
    React项目中实现多语言支持
    【WPF】大量Canvas转换为本地图片遇到的问题
    【C#】【分享】 XX分钟学会C#
    【WPF】一些拖拽实现方法的总结(Window,UserControl)
    【WPF】 InkCanvas 书写毛笔效果
    js中this指向问题
    js原型浅谈理解
  • 原文地址:https://www.cnblogs.com/wanderingzj/p/5295689.html
Copyright © 2020-2023  润新知