• 9,多态


    一、名词解释


      1,多态

        1)静态多态:a) 函数多态:函数重载,运算符重载

              b) 模板多态:函数模板,类模板

        2)动态多态:虚函数(切记:只有通过指针或引用才能实现动态多态性。"对象.虚函数“ 和 ”类::虚函数“ 这两种形式不能实现多态)

      2,静态类型与动态类型

        1) 静态类型:声明对象时,对象被赋予的类型,编译时期确定。

        2) 动态类型:指针或引用所指对象的类型。常常是,基类指针指向派生类对象,那么这个指针(或引用)的静态类型是基类指针或引用,其动态类型就是派生类指针或引用。

      3,静态绑定与动态绑定

       1) 静态绑定:亦或静态联编。在编译程序时就根据调用函数提供的信息,把它所对应的具体函数确定下来(编译时期,把调用函数名与具体函数绑定在一起。)

       2) 动态绑定:亦或动态联编。在编译程序时还不能确定函数调用所对应的具体函数,只有在程序运行过程中才能够确定函数调用所对应的具体函数。

       巧计:动态绑定只发生在继承类的虚函数上(且这个虚函数无缺省参数),其它一概是静态绑定。

    二、实例解析


      我们看下面这一个示例。

     1 #include <iostream>
     2 using  namespace std;
     3 class A
     4 {
     5 public:
     6     virtual void func(int val = 1) {
     7         cout<< "A->" << val << endl;
     8     }
     9     virtual void test() {
    10         func();
    11     }
    12 
    13     virtual void func2() {
    14         cout << "Base A" << endl;
    15     }
    16 };
    17 class B : public A
    18 {
    19 public:
    20     void func(int val = 0) {
    21         cout<<"B->"<<val <<endl;
    22     }
    23     void func2() {
    24         cout << "Base B" << endl;
    25     }
    26 };
    27 int main(int argc ,char* argv[])
    28 {
    29     cout << "Virtual function with default parameters." << endl;
    30     B*p = new B;    //p的静态类型和动态类型都是 B*
    31     p->test();
    32     p->func2();
    33 
    34     cout << endl << "reference work with virtual function." << endl;
    35     B objectB;
    36     A &objectA = objectB;   //objectA的静态类型是 A&, 动态类型是 B&
    37     objectA.func();
    38     objectA.func2();
    39 
    40     cout << endl << "object work with virtual functions." << endl;
    41     A objectA2;
    42     objectA2.func();
    43     objectA2.func2();
    44     return 0;
    45 }

      1,静态类型,动态类型已在程序中注释

      2,程序输出

      

      1)第一输出

        这一输出是测试带有缺省参数的虚函数。我们看到,基类虚函数的缺省参数没有被修改。(这一情况,总说纷纭,有的把他归于动态绑定,有的归于静态绑定)。所以,尽量不要定义继承而来的缺省参数。

      2)第二输出

        用基类引用调用派生类虚函数,实现多态。

      3)第三输出

        基类对象调用自家成员函数,忽略virtual关键字。

    三、构造函数,析构函数不要调用虚函数


      我们先看下示例代码。

     1 #include "stdio.h"
     2 class Base
     3 {
     4 public:
     5     Base()
     6     {
     7         Init();
     8     }
     9     virtual void Init()
    10     {
    11         printf("Base Init
    ");
    12     }
    13     void func()
    14     {
    15         printf("Base func
    ");
    16     }
    17 };
    18 class Derived: public Base
    19 {
    20 public:
    21     virtual void Init()
    22     {
    23         printf("Derived Init
    ");
    24     }
    25     void func()
    26     {
    27         printf("Derived func
    ");
    28     }
    29 };
    30  
    31 int main()
    32 {
    33     Derived d;
    34     ((Base *)&d)->func();
    35      
    36     return 0;
    37 }

      程序输出为:

    Base Init
    Base func

      我们从输出中可以看到,此时的虚函数并没有发挥它动态绑定的效果。这是因为,在基类构造的时候,虚函数是非虚,不会走到派生类中,既是采用的静态绑定。显然的是:当我们构造一个子类的对象时,先调用基类的构造函数,构造子类中基类部分,子类还没有构造,还没有初始化,如果在基类的构造中调用虚函数,如果可以的话就是调用一个还没有被初始化的对象,那是很危险的,所以C++中是不可以在构造父类对象部分的时候调用子类的虚函数实现。但是不是说你不可以那么写程序,你这么写,编译器也不会报错。只是你如果这么写的话编译器不会给你调用子类的实现,而是还是调用基类的实现。

      在析构函数中也不要调用虚函数。在析构的时候会首先调用子类的析构函数,析构掉对象中的子类部分,然后在调用基类的析构函数析构基类部分,如果在基类的析构函数里面调用虚函数,会导致其调用已经析构了的子类对象里面的函数,这是非常危险的。

     

     

  • 相关阅读:
    走出软件作坊
    [Flash入门基本动画]第8课
    [Flash入门基本动画]第6课
    数据中心十项节能妙招
    全面实施虚拟化的五个步骤
    Javascript的匿名函数
    TSQL调试器重返SQL Server 2008
    SQL Server 2008(BI)PPT下载
    通过数据中心整合和虚拟化实现高密度服务器配置
    [Flash入门基本动画]第7课
  • 原文地址:https://www.cnblogs.com/letgo/p/5808322.html
Copyright © 2020-2023  润新知