C++多态

本文介绍了C++中的多态性和纯虚函数的概念,包括如何通过虚函数实现多态,纯虚函数的定义及用途,以及抽象类的特点。同时,还探讨了RTTI机制和类型信息的获取。
4.1
C++多态----------“一个接口,多种方法”
-------
导航:
     1、多态性
     2、C++纯虚函数和抽象类
     3、typeid运算符
     4、RTTI机制


1、*多态性-----“一个接口,多种方法”
       -----程序在运行时才决定调用的函数,它是面向对象编程            领域的核心概念。
多态(polymorphism)------字面意思是  多种形状


2、C++多态性是通过虚函数来实现的,虚函数允许子类重新定义成员函数,而子类重新定义父类的做法称为遮蔽或者重写。
---可以理解为:子类可以重新定义父类成员函数,重新定义有两种,一种是之前学的函数重载,这一种通过名称相同但是参数不同,属于一种静态的多态(也可以说没有体现出多态性)。另一种重新定义的方法是重新定义虚函数,这体现了C++的多态性。


3、需要说明的是,多态性体现在“向上转型”这个时候,最常用的用法是声明基类的指针,利用该指针指向一个派生类对象,(也就是向上转型),调用相应的虚函数,可以根据指向的派生类的不同而实现不同的方法。


   提一下:引用也可以实现多态,但是引用一开始就要赋初值并且要从一而终,几个对象就要几个引用,不灵活。


4、
   为了方便,可以只将基类中的函数声明为虚函数,这样所有类中具有遮蔽关系的同名函数都将自动称为虚函数。------就是子类也具有传递性,虚函数也会传递。


   当在基类中定义了虚函数时,如果派生类没有定义新的函数来遮蔽次函数,那么将使用基类的虚函数。
   
   只有派生类的虚函数遮蔽基类的虚函数(函数原型相同)才能构成多态(通过基类指针访问派生类函数)。例如基类虚函数的原型为virtual void func();,派生类虚函数的原型为virtual void func(int);,那么当基类指针 p 指向派生类对象时,语句p -> func(100);将会出错,而语句p -> func();将调用基类的函数。


   构造函数不能是虚函数。对于基类的构造函数,它仅仅是在派生类构造函数中被调用,这种机制不同于继承。也就是说,派生类不继承基类的构造函数,将构造函数声明为虚函数没有什么意义。


   析构函数可以声明为虚函数,而且有时候必须要声明为虚函数。


5、构成多态的条件:
   1、必须存在继承关系 
   2、继承关系中必须有同名虚函数,并且他们是遮蔽函数。(原      型相同,包括参数)
   3、存在基类的指针,通过该指针调用虚函数


6、C++纯虚函数和抽象类
   
   语法格式为:
         virtual 返回值类型 函数名 (函数参数) = 0;


   纯虚函数没有函数体,只有函数声明,在这个虚函数结尾加上=0,表示此函数是纯虚函数,但是不表示函数返回值为0,只是形式上的作用,告诉编译系统这是纯虚函数。
  
   包含纯虚函数的类称为“抽象类”(Abstract Class).
----因为它无法实例化,也就是无法创建对象。原因很明显,纯虚函数没有函数体,不是完整的函数,无法调用,也无法为其分配内存空间。
   抽象类通常是作为基类,让基类区实现纯虚函数,派生类必须实现纯虚函数才能被实例化。
   
   需要注意的是,如果基类有两个纯虚函数,派生类中只定义了一个纯虚函数,该派生类仍旧是抽象类。
   抽象基类除了约束派生类的功能,还可以实现多态。请注意第 51 行代码,指针 p 的类型是 Line,但是它却可以访问派生类中的 area() 和 volume() 函数,正是由于在 Line 类中将这两个函数定义为纯虚函数;如果不这样做,51 行后面的代码都是错误的。我想,这或许才是C++提供纯虚函数的主要目的。


7、关于纯虚函数的几点说明
   1、一个纯虚函数就可以使类成为抽象基类,但是抽象基类中除了包含纯虚函数外,还可以包含其它的成员函数(虚函数或普通函数)和成员变量。




   2、只有类中的虚函数才能被声明为纯虚函数,普通成员函数和顶层函数均不能呢个声明为纯虚函数。


8、typeid运算符(type+id)
   
   typeid运算符用来获取一个表达式的类型信息,类型信息对于编程语言非常重要,它描述了数据的各种属性:


   对于基本类型(int、float 等C++内置类型)的数据,类型信息所包含的内容比较简单,主要是指数据的类型。
   
对于类类型的数据(也就是对象),类型信息是指对象所属的类、所包含的成员、所在的继承关系等。


   类型信息是创建数据的模板,数据占用多大内存、能进行什么样的操作、该如何操作等,这些都由它的类型信息决定。


   


   typeid( dataType )


   typeid( expression )




   dataType 是数据类型,expression 是表达式,这和 sizeof 运   算符非常类似,只不过 sizeof 有时候可以省略括号( ),而    typeid 必须带上括号。


   type_info 类的几个成员函数,下面是对它们的介绍:
name() 用来返回类型的名称。
raw_name() 用来返回名字编码(Name Mangling)算法产生的新名称。
hash_code() 用来返回当前类型对应的 hash 值。hash 值是一个可以用来标志当前类型的整数,有点类似学生的学号、公民的身份证号、银行卡号等。不过 hash 值有赖于编译器的实现,在不同的编译器下可能会有不同的整数,但它们都能唯一地标识某个类型。


   扩展:type_info 类至少要有如下所示的 4 个 public 属性的成员函数。
   


   1) 原型:const char* name() const;


            返回一个能表示类型名称的字符串。


   2) 原型:bool before (const type_info& rhs) const;
            判断一个类型是否位于另一个类型的前面,rhs 参数            是一个 type_info 对象的引用。




   3) 原型:bool operator== (const type_info& rhs) const;
重载运算符“==”,判断两个类型是否相同,rhs 参数是一个 type_info 对象的引用。


   4) 原型:bool operator!= (const type_info& rhs) const;
重载运算符“!=”,判断两个类型是否不同,rhs 参数是一个 type_info 对象的引用。






   *******
   划重点:即使将派生类指针 p2 赋值给基类指针 p1,p1 的类型仍然为 Base*。
   对象和类名的哈希值是一样的。




9、RTTI机制(运行时类别识别)


   在程序运行后确定对象的类型信息的机制称为运行时类别识别。C++中,只有类中包含了虚函数时才会启用RTTI机制,其他所有情况都可以在编译阶段确定类型信息。


10、函数编译远离和成员函数的实现
    
    C++和C语言的编译方式不同。C语言中的函数在编译时名字不变,或者只是简单的加一个下划线_(不同的编译器有不同的实现),例如,func() 编译后为 func() 或 _func()。
    
    而C++中的函数在编译时会根据它所在的命名空间、它所属的类、以及它的参数列表(也叫参数签名)等信息进行重新命名,形成一个新的函数名。这个新的函数名只有编译器知道,对用户是不可见的。对函数重命名的过程叫做名字编码(Name Mangling),是通过一种特殊的算法来实现的。
    
    Name Mangling 的算法是可逆的,既可以通过现有函数名计算出新函数名,也可以通过新函数名逆向推演出原有函数名。Name Mangling 可以确保新函数名的唯一性,只要函数所在的命名空间、所属的类、包含的参数列表等有一个不同,最后产生的新函数名也不同。


    


    成员函数最终被编译成与对象无关的“全局函数”,如果函数体中没有成员变量,那问题就很简单,不用对函数做任何处理,直接调用即可。如果成员函数中使用到了成员变量该怎么办呢?成员变量的作用域不是全局,不经任何处理就无法在函数内部访问。
------------C++规定,编译成员函数时要额外添加一个参数,把当前对象的“指针”传递进去,通过指针来访问成员变量。


   假设 Demo 类有两个 int 型的成员变量,分别是 a 和 b,并且在成员函数 display() 中使用到了,如下所示:


 
    
void Demo::display(){


    cout<<a<<endl;


    cout<<b<<endl;


}




    编译后的代码类似于:
    
void new_function_name(Demo * const p){
 
    //通过指针p来访问a、b
    
    cout<<p->a<<endl;
    
    cout<<p->b<<endl;


}


    使用obj.display()调用函数时,也会被编译成类似下面的形式:
    
    new_function_name(&obj);






    这样通过传递对象指针就完成了成员函数和成员变量的关联。这与我们从表明上看到的刚好相反,通过对象调用成员函数时,不是通过对象找函数,而是通过函数找对象。


      这一切都是隐式完成的,对程序员来说完全透明,就好像这个额外的参数不存在一样。
    
      最后需要提醒的是,Demo * const p中的 const 表示指针不能被修改,p 只能指向当前对象,不能指向其他对象。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值