精华内容
下载资源
问答
  • C++多态性的分类

    千次阅读 2015-03-07 10:30:26
    多态(polymorphism)一词最初来源于希腊语polumorphos,含义是具有多种形式或形态情形。在程序设计领域,一个广泛认可定义是 “一种将不同特殊行为和单个泛化记号相关联能力”。和纯粹面向对象程序设计...
    多态(polymorphism)一词最初来源于希腊语polumorphos,含义是具有多种形式或形态的情形。在程序设计领域,一个广泛认可的定义是 “一种将不同的特殊行为和单个泛化记号相关联的能力”。和纯粹的面向对象程序设计语言不同,C++中的多态有着更广泛的含义。除了常见的通过类继承虚函数机制生效于运行期的动态多态(dynamic
     polymorphism)外,模板也允许将不同的特殊行为和单个泛化记号相关联,由于这种关联处理于编译期而非运行期,因此被称为静态多态 (static
     polymorphism)。  
    事实上,带变量的宏函数重载机制也允许将不同的特殊行为和单个泛化记号相关联。然而,习惯上 我们并不将它们展现出来的行为称为多态(或静态多态)。今天,当我们谈及多态时,如果没有明确所指,默认就是动态多态,而静态多态则是指基于模板的多态。 不过,在这篇以C++各种多态技术为主题的文章中,我们首先还是回顾一下C++社群争论已久的另一种“多态”:函数多态(function polymorphism),以及更不常提的“宏多态(macro polymorphism)”。 

    理解:
    多态包括三种,分别是动态多态,静态多态和宏多态

    动态多态:只有动态绑定一种,通过虚函数+继承实现,子类在运行时通过虚函数调用具体实现;
    静态多态:包括函数模板函数/运算符重载两种,函数模版技术利用模板+编译器多态生成不同的执行代码,函数重载通过函数参数的不同确定调用关系
    宏多态:  使用整句替换的方法生成静态多态

    相对于面向对象编程中,以显式接口运行期多态(虚函数)实现动态多态,在模板编程及泛型编程中,是以隐式接口编译器多态来实现静态多态。

    函数多态 

    也就是我们常说的函数重载(function overloading)。基于不同的参数列表,同一个函数名字可以指向不同的函数定义:  
    // overload_poly.cpp 
    #include <iostream>
    #include <string>    // 定义两个重载函数 
    int my_add(int a, int b)
    {
        return a + b;
    }
    int my_add(int a, std::string b)
    {
        return a + atoi(b.c_str());
    }
    int main()
    {
        int i = my_add(1, 2);                // 两个整数相加 
        int s = my_add(1, "2");              // 一个整数和一个字符串相加 
        std::cout << "i = " << i << "/n";
        std::cout << "s = " << s << "/n";
    } 
    根据参数列表的不同(类型、个数或兼而有之),my_add(1, 2)和my_add(1, "2")被分别编译为对my_add(int, int)和my_add(int, std::string)的调用。实现原理在于编译器根据不同的参数列表对同名函数进行名字重整,而后这些同名函数就变成了彼此不同的函数。比方说,也许 某个编译器会将my_add()函数名字分别重整为my_add_int_int()和my_add_int_str()。 

    宏多态 

    带变量的宏可以实现一种初级形式的静态多态:  
    // macro_poly.cpp 
    #include <iostream>
    #include <string>
    // 定义泛化记号:宏ADD 
    #define ADD(A, B) (A) + (B);
    int main()
    {
        int i1(1), i2(2);
        std::string s1("Hello, "), s2("world!");
        int i = ADD(i1, i2);                        // 两个整数相加 
        std::string s = ADD(s1, s2);                // 两个字符串“相加” 
        std::cout << "i = " << i << "/n";
        std::cout << "s = " << s << "/n";
    }
    当程序被编译时,表达式ADD(i1, i2)和ADD(s1, s2)分别被替换为两个整数相加两个字符串相加的具体表达式。整数相加体现为求和,而字符串相加则体现为连接。程序的输出结果符合直觉:  
    1 + 2 = 3
    Hello, + world! = Hello, world! 

    动态多态
    这就是众所周知的的多态。现代面向对象语言对这个概念的定义是一致的。其技术基础在于继承机制和虚函数。例如,我们可以定义一个抽象基类Vehicle和两个派生于Vehicle的具体类Car和Airplane: 
    // dynamic_poly.h 
    #include <iostream>
    // 公共抽象基类Vehicle 
    class Vehicle
    {
    public:
        virtual void run() const = 0;
    };
    // 派生于Vehicle的具体类Car 
    class Car: public Vehicle
    {
    public:
        virtual void run() const
        {
            std::cout << "run a car/n";
        }
    };
    // 派生于Vehicle的具体类Airplane 
    class Airplane: public Vehicle
    {
    public:
        virtual void run() const
        {
            std::cout << "run a airplane/n";
        }
    }; 
    客户程序可以通过指向基类Vehicle的指针(或引用)来操纵具体对象。通过指向基类对象的指针(或引用)来调用一个虚函数,会导致对被指向的具体对象之相应成员的调用: 
    // dynamic_poly_1.cpp 
    #include <iostream>
    #include <vector>
    #include "dynamic_poly.h"
    // 通过指针run任何vehicle 
    void run_vehicle(const Vehicle* vehicle)
    {
        vehicle->run();            // 根据vehicle的具体类型调用对应的run() 
    }
    int main()
    {
        Car car;
        Airplane airplane;
        run_vehicle(&car);         // 调用Car::run() 
        run_vehicle(&airplane);    // 调用Airplane::run() 
    }
    此 例中,关键的多态接口元素为虚函数run()。由于run_vehicle()的参数为指向基类Vehicle的指针,因而无法在编译期决定使用哪一个版 本的run()。在运行期,为了分派函数调用,虚函数被调用的那个对象的完整动态类型将被访问。这样一来,对一个Car对象调用 run_vehicle(),实际上将调用Car::run(),而对于Airplane对象而言将调用Airplane::run()。 
    或许动态多态最吸引人之处在于处理异质对象集合的能力:  
    // dynamic_poly_2.cpp 
    #include <iostream>
    #include <vector>
    #include "dynamic_poly.h"
    // run异质vehicles集合 
    void run_vehicles(const std::vector<Vehicle*>& vehicles)
    {
        for (unsigned int i = 0; i < vehicles.size(); ++i)
        {
            vehicles[i]->run();     // 根据具体vehicle的类型调用对应的run() 
        }
    }
    int main()
    {
        Car car;
        Airplane airplane;
        std::vector<Vehicle*> v;    // 异质vehicles集合 
        v.push_back(&car);
        v.push_back(&airplane);
        run_vehicles(v);            // run不同类型的vehicles 
    }
    在run_vehicles()中,vehicles[i]->run()依据正被迭代的元素的类型而调用不同的成员函数。这从一个侧面体现了面向对象编程风格的优雅。 
    静态多态 
    如果说动态多态是通过虚函数来表达共同接口的话,那么静态多态则是通过“彼此单独定义但支持共同操作的具体类”来表达共同性,换句话说,必须存在必需的同名成员函数。  对于相关的对象类型,直接实现它们各自的定义,不需要共有基类,甚至可以没有任何关系。只需要各个具体类的实现中要求相同的接口声明,这里的接口称之为隐式接口。客户端把操作这些对象的函数定义为模板,当需要操作什么类型的对象时,直接对模板指定该类型实参即可(或通过实参演绎获得)。
    我们可以采用静态多态机制重写上一节的例子。这一次,我们不再定义vehicles类层次结构,相反,我们编写彼此无关的具体类Car和Airplane(它们都有一个run()成员函数):  
    // static_poly.h 
    #include <iostream>
    //具体类Car 
    class Car
    {
    public:
        void run() const
        {
            std::cout << "run a car/n";
        }
    };
    //具体类Airplane 
    class Airplane
    {
    public:
        void run() const
        {
            std::cout << "run a airplane/n";
        }
    };
    
    run_vehicle()应用程序被改写如下:
    // static_poly_1.cpp 
    #include <iostream>
    #include <vector>
    #include "static_poly.h"
    // 通过引用而run任何vehicle 
    template <typename Vehicle>
    void run_vehicle(const Vehicle& vehicle)
    {
        vehicle.run();            // 根据vehicle的具体类型调用对应的run() 
    }
    int main()
    {
        Car car;
        Airplane airplane;
        run_vehicle(car);         // 调用Car::run() 
        run_vehicle(airplane);    // 调用Airplane::run() 
    } 
    现在Vehicle用作模板参数非公共基类对象(事实上,这里的Vehicle只是一个符合直觉的记号而已,此外别无它意)。经过编译器处理后,我们最终 会得到run_vehicle<Car>()和 run_vehicle<Airplane>()两个不同的函数。这和动态多态不同,动态多态凭借虚函数分派机制在运行期只有一个 run_vehicle()函数。  
    我们无法再透明地处理异质对象集合了,因为所有类型都必须在编译期予以决定。不过,为不同的vehicles引入不同的集合只是举手之劳。由于无需再将集合元素局限于指针或引用,我们现在可以从执行性能和类型安全两方面获得好处: 
    // static_poly_2.cpp
    #include <iostream>
    #include <vector>
    #include "static_poly.h"
    // run同质vehicles集合 
    template <typename Vehicle>
    void run_vehicles(const std::vector<Vehicle>& vehicles)
    {
        for (unsigned int i = 0; i < vehicles.size(); ++i) 
        {
            vehicles[i].run();            // 根据vehicle的具体类型调用相应的run() 
        }
    }
    int main()
    {
        Car car1, car2;
        Airplane airplane1, airplane2;
    
        std::vector<Car> vc;              // 同质cars集合 
        vc.push_back(car1);
        vc.push_back(car2);
        //vc.push_back(airplane1);        // 错误:类型不匹配 
        run_vehicles(vc);                 // run cars 
    
        std::vector<Airplane> vs;         // 同质airplanes集合 
        vs.push_back(airplane1);
        vs.push_back(airplane2);
        //vs.push_back(car1);             // 错误:类型不匹配 
        run_vehicles(vs);                 // run airplanes 
    }
    两种多态机制的结合使用   

    在 一些高级C++应用中,我们可能需要结合使用动态多态静态多态两种机制,以期达到对象操作的优雅、安全和高效。例如,我们既希望一致而优雅地处理 vehicles的run问题,又希望“安全而高效”地完成给飞行器(飞机、飞艇等)进行“空中加油”这样的高难度动作。为此,我们首先将上面的 vehicles类层次结构改写如下:  
    // dscombine_poly.h 
    #include <iostream>
    #include <vector>
    // 公共抽象基类Vehicle 
    class Vehicle
    {
        public:
        virtual void run() const = 0;
    };
    // 派生于Vehicle的具体类Car 
    class Car: public Vehicle
    {
    public:
        virtual void run() const
        {
            std::cout << "run a car/n";
        }
    };
    // 派生于Vehicle的具体类Airplane 
    class Airplane: public Vehicle
    {
    public:
        virtual void run() const
        {
            std::cout << "run a airplane/n";
        }
        void add_oil() const
        {
            std::cout << "add oil to airplane/n";
        }
    };
    // 派生于Vehicle的具体类Airship 
    class Airship: public Vehicle
    {
    public:
        virtual void run() const
        {
            std::cout << "run a airship/n";
        }
        void add_oil() const
        {
            std::cout << "add oil to airship/n";
        }
    };
    
    我们理想中的应用程序可以编写如下:
    // dscombine_poly.cpp 
    #include <iostream>
    #include <vector>
    #include "dscombine_poly.h"
    
    // run异质vehicles集合 
    void run_vehicles(const std::vector<Vehicle*>& vehicles)
    {
        for (unsigned int i = 0; i < vehicles.size(); ++i)
        {
            vehicles[i]->run();                 // 根据具体的vehicle类型调用对应的run() 
        }
    }
    
    // 为某种特定的aircrafts同质对象集合进行“空中加油” 
    template <typename Aircraft>
    void add_oil_to_aircrafts_in_the_sky(const std::vector<Aircraft>& aircrafts)
    {
        for (unsigned int i = 0; i < aircrafts.size(); ++i)
        {
            aircrafts[i].add_oil();
        }
    }
    
    int main()
    {
        Car car1, car2;
        Airplane airplane1, airplane2;
    
        Airship airship1, airship2;
        std::vector<Vehicle*> v;                // 异质vehicles集合 
        v.push_back(&car1);
        v.push_back(&airplane1);
        v.push_back(&airship1);
        run_vehicles(v);                        // run不同种类的vehicles 
    
        std::vector<Airplane> vp;               // 同质airplanes集合 
        vp.push_back(airplane1);
        vp.push_back(airplane2);
        add_oil_to_aircrafts_in_the_sky(vp);    // 为airplanes进行“空中加油” 
    
        std::vector<Airship> vs;                // 同质airships集合 
        vs.push_back(airship1);
        vs.push_back(airship2);
        add_oil_to_aircrafts_in_the_sky(vs);    // 为airships进行“空中加油” 
    } 

    我 们保留了类层次结构,目的是为了能够利用run_vehicles()一致而优雅地处理异质对象集合vehicles的run问题。同时,利用函数模板 add_oil_to_aircrafts_in_the_sky<Aircraft>(),我们仍然可以处理特定种类的vehicles — aircrafts(包括airplanes和airships)的“空中加油”问题。其中,我们避开使用指针,从而在执行性能和类型安全两方面达到了预 期目标。
     
    结语  
      
    可能你是第一次看到宏多态(macro polymorphism)这个术语。不必讶异 — 也许我就是造出这个术语的“第一人”。显然,带变量的宏(或类似于函数的宏或伪函数宏)的替换机制除了免除小型函数的调用开销之外,也表现出了类似的多态 性。在我们上面的例子中,字符串相加所表现出来的符合直觉的连接操作,事实上是由底部运算符重载机制(operator overloading)支持的。值得指出的是,C++社群中有人将运算符重载所表现出来的多态称为ad hoc polymorphism。  

    动态多态只需要一个多态函数,生成的可执行代码尺寸较小,静态多态必须针对不同的类型产生不同的模板实体,尺寸会大一些,但生成的代码会更快,因为无需通过 指针进行间接操作。静态多态比动态多态更加类型安全,因为全部绑定都被检查于编译期。正如前面例子所示,你不可将一个错误的类型的对象插入到从一个模板实例化而来的容器之中。此外,正如你已经看到的那样,动态多态可以优雅地处理异质对象集合而静态多态可以用来实现安全、高效的同质对象集合操作。 

    静 态多态为C++带来了泛型编程(generic programming)的概念。泛型编程可以认为是“组件功能基于框架整体而设计”的模板编程。STL就是泛型编程的一个典范。STL是一个框架,它提 供了大量的算法、容器和迭代器,全部以模板技术实现。从理论上讲,STL的功能当然可以使用动态多态来实现,不过这样一来其性能必将大打折扣。  

    静 态多态还为C++社群带来了泛型模式(generic patterns)的概念。理论上,每一个需要通过虚函数和类继承而支持的设计模式都可以利用基于模板的静态多态技术(甚至可以结合使用动态多态和静态多态两种技术)而实现。正如你看到的那样,Andrei Alexandrescu的天才作品Modern C++ Design: Generic Programming and Design Patterns Applied (Addison-Wesley)和Loki程序库已经走在了我们的前面

     
    展开全文
  • 多态性

    2017-08-01 10:18:19
    多态性的概念: v 多态性是面向对象程序设计的重要特征之...多态性的分类:强制多态、重载多态、类型参数化多态以及包含多态。 问题举例——复数的运算 v 用“+”、“-”能够实现复数的加减运算吗? v 

    多态性的概念:

    多态性是面向对象程序设计的重要特征之一。

    多态性是指发出同样的消息被不同类型的对象接收时有可能导致完全不同的行为。

    多态的实现:

    函数重载

    运算符重载

    虚函数

    多态性的分类:强制多态、重载多态、类型参数化多态以及包含多态。

    问题举例——复数的运算

    +”、“-”能够实现复数的加减运算吗?

    实现复数加减运算的方法
              ——重载+”、“-”运算符

     

    运算符重载的实质:

    运算符重载是对已有的运算符赋予多重含义

    必要性

    C++中预定义的运算符其运算对象只能是基本数据类型,而不适用于用户自定义类型(如类)

    实现机制

    将指定的运算表达式转化为对运算符函数的调用,运算对象转化为运算符函数的实参。

    编译系统对重载运算符的选择,遵循函数重载的选择原则。

    两种形式:

    重载为类的成员函数(非静态)

    重载为非成员函数

    运算符函数:

    声明形式

    函数类型  operator 运算符(形参)

    {

           ......

    }

    重载为类成员函数时  
    参数个数=原操作数个数-1 (后置++--除外)

    重载为非成员函数时  参数个数=原操作数个数,且至少应该有一个自定义类型的形参。

    运算符成员函数的设计:

    双目运算符 B

    如果要重载 B 为类成员函数,使之能够实现表达式 oprd1 B oprd2,其中 oprd1 A 类对象,则 B 应被重载为A 类的成员函数,形参类型应该是 oprd2 所属的类型。

    经重载后,表达式 oprd1 B oprd2 相当于 oprd1.operator B(oprd2)

     

    前置单目运算符 U

    如果要重载 U 为类成员函数,使之能够实现表达式 U oprd,其中 oprd A类对象,则 U 应被重载为 A 类的成员函数,无形参。

    经重载后,
    表达式 U oprd 相当于 oprd.operator U()

    后置单目运算符 ++--

    如果要重载 ++--为类成员函数,使之能够实现表达式  oprd++  oprd-- ,其中 oprd A类对象,则 ++--  应被重载为 A 类的成员函数,且具有一个 int 类型形参。

    经重载后,表达式  oprd++ 相当于  oprd.operator ++(0)

    运算符非成员函数的设计:

    双目运算符 B重载后,
    表达式oprd1 B oprd2 
    等同于operator B(oprd1,oprd2 )

    前置单目运算符 B重载后,
    表达式 B oprd 
    等同于operator B(oprd )

    后置单目运算符 ++--重载后,
    表达式 oprd B 
    等同于operator B(oprd,0 )

    虚函数:

    是非静态的成员函数。

    在类的声明中,在函数原型之前写virtual

    virtual 只用来说明类声明中的原型,不能用在函数实现时。

    具有继承性,基类中声明了虚函数,派生类中无论是否说明,同原型函数都自动为虚函数。

    本质:不是重载声明而是覆盖。

    调用方式:通过基类指针或引用,执行时会
    根据指针指向的对象的类,决定调用哪个函数。

    虚析构函数:

    为什么需要虚析构函数?

    可能通过基类指针删除派生类对象;

    如果你打算允许其他人通过基类指针调用对象的析构函数(通过delete这样做是正常的),就需要让基类的析构函数成为虚函数,否则执行delete的结果是不确定的

    虚析构函数:

    #include

    class BaseClass {

    public:

    virtual ~BaseClass() {

    cout << "~BaseClass()" << endl;

    }

    };

    class DerivedClass : public BaseClass {

    public:

    ~DerivedClass() {

    cout << "~DerivedClass()" << endl;

    }

    };

    void main()

    {BaseClass* bp = new DerivedClass;

    delete bp;

    }//结果~DerivedClass()/n~BaseClass()

     

    抽象类带有纯虚函数的类称为抽象类。

    class  类名

     {

         virtual 类型 函数名(参数表)=0;

                                              //纯虚函数

         ...

    }

    作用:

    抽象类为抽象和设计的目的而声明,将有关的数据和行为组织在一个继承层次结构中,保证派生类具有要求的行为。

    对于暂时无法实现的函数,可以声明为纯虚函数,留给派生类去实现。

    注意

    抽象类只能作为基类来使用。

    不能声明抽象类的对象,但是可以定义一个抽象类的指针和引用,通过指针或引用,就可以指向并访问派生类的对象,进而访问派生类的成员,这种访问是具有多态性的。

    构造函数不能是虚函数,析构函数可以是虚函数。

     

     

     

    展开全文
  • C++的多态性

    千次阅读 2019-05-09 21:14:01
    多态性是面向对象编程中重要特征之一。其是用父类型别指针(或引用)指向其子类实例,然后通过父类指针调用实际子类成员函数。这种由于调用子类不同可以让父类指针有“多种形态”,这是一种泛型技术...

     概述

          多态性是面向对象编程中的重要特征之一。其是用父类型别的指针(或引用)指向其子类的实例,然后通过父类的指针调用实际子类的成员函数。这种由于调用子类的不同可以让父类的指针有“多种形态”,这是一种泛型技术。

          多态实现方式:

    • 函数重载
    • 运算符重载
    • 虚函数

    多态实现

    •     多态分类

         1. 编译时多态(静态联编)

             : 通过函数重载 和 运算符重载实现。

         2. 运行时多态(动态联编)

             : 通过类的继承关系 和 虚函数实现。

         联编:也称绑定,这里是指计算机程序自身彼此关联的过程,即将函数名对象关联的过程。

    •     编译时多态的实现

    函数重载

    概念

    两个函数在同一作用域,函数名相同,参数列表不同。

    表达方式

    在一个类中重载

    在不同类中重载

    基类的成员函数在派生类中重载

     

    被重载的同名函数在编译期间区分,编译器会通过:1. 根据实参类型;2. 使用类作用域限定符“::”区分;3. 根据调用函数的类对象区分

     

                  运算符重载:

    运算符重载实质上是函数重载。        

    语法: 函数类型 operator 运算符名称(参数列表){ // 重载处理 }

    说明:

    • 运算符重载中:运算符顺序和优先级不能更改,但参数和返回类型可以重载;
    • 运算符参数都是内部类型时不能重载;
    • 基本数据之间关系是确定的,若允许在其上重新定义,那么基本数据类型的内在关系将混乱;
    • C++规定::: .* ?: 4个运算符不能重载;
    •     运行时多态的实现

    实现条件:

    • 基类中必须存在虚函数,派生类必须对基类中的虚函数进行重写;

    重写:两个函数作用域不同(分别在基类和派生类),函数名相同,参数列表相同,用virtual修饰即基类一定有虚函数

    规则:

    1. 返回值类型相同;

    2. 派生类重写基类的虚函数的 关键字"virtual" 可加可以不加;

    3. 派生类虚函数必须与被重写的基类函数原型一致(返回值,函数名字,参数类型必须完全相同);

     例外:协变:基类虚函数返回基类对象的指针或者引用,派生类虚函数返回派生类对象的指针或者引用。// 仅适用于返回值,而不是参数。
                                                                    
          析构函数 :如果基类中的析构函数是虚函数,派生类虚构函数提供构成重写,且构成重写的虚函数的函数名字不同。

    4. 派生类虚函数的访问权限可以与基类虚函数的访问权限不同,基类中虚函数访问权限必须是公有的。

    • 必须通过基类指针或者引用来调用虚函数。

       虚函数的注意事项:

    • 构造函数不能虚函数。因为构造派生类对象方式不同于继承时,先调用派生类的构造函数,然后派生类的构造函数将使用基类的一个构造函数。而且虚函数调用是在部分信息下完成工作的机制,允许我们只知道接口而不知道对象的确切类型。 要创建一个对象,你需要知道对象的完整信息。 特别是,你需要知道你想要创建的确切类型。 因此,构造函数不应该被定义为虚函数。
    • 析构函数可以为虚函数,且通常声明为虚函数。将析构函数声明为虚函数保证当我们new一个子类,然后使用基类指针指向该子类对象,释放基类指针时可以释放掉子类的空间,防止内存泄漏。

     

    • 友元函数不能为虚函数。因为友元函数不是类的成员函数,因为虚函数仅适合用于有继承关系的对象;
    • 静态成员函数不能为虚函数,因为静态成员函数没有this指针,不受限与任一对象的;
    • 内联函数不能为虚函数,因为内联函数不能在运行中动态确定;
    • 如果派生类没有重写虚函数,则将使用该函数的基类版本。如果派生类在一条派生链中,则使用最新虚函数版本;
    • 派生类被重写的方法,不仅隐藏参数列表相同的同名基类方法,而且隐藏参数列表不相同的同名基类方法;

          纯虚函数与抽象类:

      纯虚函数

    语法:virtual 函数返回值 函数名(参数列表)= 0; 

    说明:

    • 声明为纯虚函数,基类中就不能在给出函数实现部分
    • C++中有一种情况:空的虚函数,空的虚函数指函数体为空,而纯虚函数压根没有函数体,有着本质区别;
    • 纯虚函数必须在派生类中重写,否则这个函数就没有存在意义。

      抽象类:

    概念:含有纯虚函数的类称为抽象类。

    作用: 抽象类是不能实例化对象的,它是为了组织一个继承的层次结构专门为派生类提供基本类型和接口函数的基类。

    所以说抽象类也被称为抽象基类,且必须被继承,否则没有存在意义。

    •     多态调用原理

         在这插入一个例子:

    #include <iostream>
    
    using namespace std;
    
    class Base{
    	virtual void Func1()
    	{}
    
    private:
    	int _val;
    };
    
    int main()
    {
    	Base b;
    	cout << sizeof(b) << endl;
    	return 0;
    }

        这调用监视窗口查看测试代码的结果:

       按我们所理解的,类中只有一个整型变量,应该占4字节,也就是说多了4个字节。

       我们再查看我们所构建的对象就能清楚明白:

       这是多存了一个指针_vfptr,这个指针所指向的是一个叫虚函数表(vtable)的东西。

       虚表的结构可以查看相关博客:https://blog.csdn.net/timecur/article/details/95981159

        派生类虚表构造过程:

    1. 先将基类虚表中虚函数拷贝一份到派生类虚表中;

    2. 如果派生类重写了基类中的虚函数,用派生类自己的虚函数替换(覆盖)相同偏移量位置同名的虚函数。

    3. 将派生类新增加的虚函数按照其在类中声明次序添加到派生类虚表的最后。

    重载

    两个函数在同一作用域,函数名相同,参数列表不同

    重写(覆盖)

    两个函数作用域不同(分别在基类和派生类),函数名相同,参数列表相同,用virtual修饰

    重定义(同名隐藏)

    1.如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无virtual

    关键字,基类的函数将被隐藏(注意别与重载混淆)。

    2.如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual

    关键字。此时,基类的函数被隐藏(注意别与覆盖混淆)。

        总结多态的调用步骤:

    1. 从指针或引用的具体的对象中取虚表的地址;

    2. 传递this指针;

    3. 从虚表中取虚指针;

    4. 调用该虚函数。

    C++11 override 和 final

    override 和 final 来修饰虚函数。

    overrid修饰虚函数,表示要强制重写虚函数,因为虚函数的意义就是实现多态,如果没有重写,虚函数就没有意义。 ---可以防止写错虚函数的函数名。

    final修饰虚函数,表示最终虚函数,之后不能再被重写。

     

    展开全文
  • 初探多态性

    2016-12-24 22:52:23
    多态性分类 多态性分为编译时多态性和运行时多态性。 编译时多态性: 一个源程序经过编译、连接,成为可执行程序过程是把可执行代码连编到一起过程。其中,在运行之前就完成联编称为静态连编,即编译时多态...

    多态性

    多态性分类

    多态性分为编译时多态性和运行时多态性。

    编译时多态性:

    一个源程序经过编译、连接,成为可执行程序的过程是把可执行代码连编到一起的过程。其中,在运行之前就完成的联编称为静态连编,即编译时多态性。

    优点是:函数调用速度快、效率高。

     

    静态多样性主要包括:重载和模板

    重载主要是函数名相同,其他的可以不同,所以,调用某一函数时,就是通过返回类型、参数类型、参数个数等来区分,以便找出这个函数。

    模板:模板一般都是定义在头文件中,有函数模板、类模板。使用template<typename T1,typenameT2,…,typename Tn>,然后用定义类和函数的方式来定义即可。

    模板注意:

             需要在成员函数定义之前进行模板声明;

             在成员函数名前缀上“类名<类型参数>::”;

            

    运行时多态性:

    动态联编是指系统在运行时实现某一动作。使用这种联编方式,直到程序运行时才知道调用哪一个函数。

    优点是:提供了更好的灵活性、问题的抽象性和程序的易维护性。

    运行时多态性主要包括:虚函数的实现。

     

    含有虚函数的类称之为抽象类,抽象类不能直接创建对象,但是可以通过继承的方式,让其派生类创建对象,然后上溯到抽象的基类。

    展开全文
  • C++多态性

    2015-07-21 09:34:43
    在面向对象的程序设计理论中,多态性的定义是:同一操作作用于不同的类的实例,将产生不同的执行结果,即不同类的对象收到相同的消息时,得到不同的结果。对象根据所接受的消息而做出动作,同样的消息被不同的对象...
  • extends继承或者implemens实现,是多态性的前提。 例如:小菜是一个学生,但同时也是一个人。小菜是一个对象,这个对象既有学生形态,也有人类形态。一个对象拥有多种形态,这就是:【对象的多态性】 多态的格式与...
  • C# 多态性

    2018-06-11 07:41:15
    静态多态性:编译时发生函数响应(调用);动态多态性:运行时发生函数响应。静态绑定(早期绑定):编译时函数和对象连接机制。 两种技术实现静态多态性:函数重载/运算符重载。函数重载:在同一范围内对相同函数...
  • JAVA—多态性

    2020-02-06 15:22:14
    方法的多态性: 1.重载:同一个方法名称,根据不同地参数类型及个数可以完成不同功能。 2.同一个方法,根据实例化子类对象不同,所完成功能也不同。 对象的多态性: 1.向上转型:子类对象变为父类对象,格式:...
  • 面向对象(高级)——对象的多态性 分类: Java 面向对象(高级)——对象的多态性 掌握对象向上转型有向下转型使用 掌握对象转型限制 对象多态性是面向对象重要性质 面向主要有以下两...
  • 为了研究体量子系统中量子的可分,利用Bell不等式给出了体量子可...利用推广的Bell算子和真纠缠分类,运用Cauchy-Schwarz不等式、线性规划等方法,给出了Bell算子平均值的绝对值上界,实现了体可分态的分类
  • (一)多态性  多态是指同样的消息被不同的对象接收时导致不同的行为。所谓消息是指对类成员函数的调用,不同的行为是指的不同的实现,也就是调用了不同的函数。 1)多态的分类  广义上说,多态性是指一段程序能够...
  • SNP(单核苷酸多态性)准确性的验证,你造吗? 【2016-12-12】 SNP(全称Single Nucleotide Polymorphisms)即单核苷酸多态性,主要是指在基因组水平上由单个核苷酸的变异引起的DNA序列多态性,包括...
  • 扩增片段长度多态性(AFLP)是一种有效分子遗传标记方法,具有快速、经济、简便、模板需要量少、重复性高、结果可靠等优点。目前AFLP在动物方面应用还不是很多,处于初级阶段。主要用于鉴定分类关系、种群遗传...
  • C++_多态性

    2016-12-01 22:46:20
    多态性是指发出同样消息被不同类型对象接收有可能导致完全不同行为。 2.分类 函数重载 运算符重载 虚函数 3.运算符重载 运算符重载是对已有运算符赋予多重含义。 必要性:C++中预定义运算符其运算对象...
  • Java之多态性

    2017-01-18 13:43:48
    多态性是允许你将父对象设置成为和它一个或更多子对象相等技术,赋值后,父对象就可以根据子对象特性以不同方式进行运作。 简单来说就是允许将子类类型指针赋值给父类类型指针,只要这样赋值产生了...
  • 面向对象——多态性

    2019-01-19 21:59:03
    一、多态性的描述  某一类事物的多种存在形态。 二、多态性的前提条件  1、类之间有继承关系  2、要有子类对父类的复写 三、多态的转型分类  1、向上转型 子→父  2、向下转型 父→子 向下转型时,...
  • 深入理解JAVA多态性

    2018-08-08 22:49:23
    多态 分类 1.编译时多态 (方法重载)method overloading 2.运行时多态 根据运行时调用该方法实例类型来决定选择调用那个方法 ...2.可扩充性,增加新子类不影响已经存在的多态性,继承性...
  • C++ 多态性

    2019-02-04 13:43:26
    多态的分类 编译时多态:静态多态(函数重载,运算符重载) 运行时多态:动态多态(虚函数) 绑定:指把一个函数名和一个存储地址联系在一起的过程 编译时多态是指绑定工作在编译阶段完成,运行时多态是指绑定...
  • 浅谈C++多态性

    2013-10-13 20:58:06
    浅谈C++多态性 分类: 面试珠玑2012-04-18 ...我们今天就会为大家详细介绍其中C++多态性的一些基本知识,以方便大家在学习过程中对此能够有一个充分的掌握。  多态性可以简单地概括为“一个接
  • C++的多态性(笔记)

    2021-03-08 21:51:15
    分类: 静态多态:函数重载和运算符重载属于静态多态,复用函数名 动态多态:派生类和虚函数实现运行时多态。 静态多态和动态多态区别: 静态多态函数地址早绑定,编译阶段确定函数地址 动态多态函数地址晚...
  • 今天我们来讲一下面向对象程序设计第三个特点:多态性。 所谓多态性就是不同对象收到相同消息时,产生不同动作。多态性有下面两种描述: 1.具有不同功能函数可以用同一个函数名。(重载、虚函数) 2.向...
  • 多态性与虚函数(一)1 多态性1.1 多态的概念1.2 多态的分类2 利用虚函数实现动态多态性2.1 虚函数的作用2.2 静态关联和动态关联2.3 虚函数的注意事项2.4 虚析构函数 1 多态性 1.1 多态的概念 多态性:向不同的对象...
  • 用从 100 个 10mers 随机引物中筛选出 13 个具有特异多态性引物对黑子南瓜种质资源遗传多态性进行了 RAPD(Random amplified polymorphic DNA)分析。结果表明,RAPD 对中国南瓜种、印度南瓜种、美洲南瓜种及黑子...
  • /*******************************************************... 利用运算符重载函数说明静态多态性 【1】多态的定义: 向不同的对象发送同一消息时,不同的对象在接收时会产生不同的行为 【2】多态的分类 从系统

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 459
精华内容 183
热门标签
关键字:

多态性的分类