【好程序员训练营学习笔记分享9】c++ 虚函数和多态

来源:互联网 发布:java线程死锁例子 编辑:程序博客网 时间:2024/06/04 18:54

<A href="http://www.goodprogrammer.org/" target="blank">ios培训</A>------我的c语言笔记,期待与您交流! 

虚函数和多态

一,多态-- 一个名字,多种语义,一个界面,多种实现;

1.类指针间的关系
关系有四种:(1)基类指针->基类对象,(2)基类指针->派生类对象,(3)派生类指针->基类对象,(4)派生类指针->派生类对象;
(1)和(4)属于正常现象,没有什么特殊情况;
2.关系(2)基类指针式可以随意指向其派生类的,但是正常情况下是不能调用派生类中的函数,经过特殊处理可以调用!!

           
    
3.关系(3)中,正常情况下派生类指针式不能指向基类对象的且不能调用父类的函数的,但是用强制类型转换可以实现派生类指针调用基类函数。
  
               wo是基类的对象
                    
二,虚函数
1.为了实现多态,使得父类指针指向各个派生的时候可以调用派生类的同名函数,实现一个接口多个实现的功能,我们不得不使用虚函数;
2.虚函数的的重载个特性:
    a.在派生类中重载基类的虚函数要求函数名,参数,参数顺序,返回值类型都一样(覆盖或者覆写)
    b.如果仅仅返回值不同,c++认为是错误重载
    c.如果函数原型不同,仅仅函数名相同,丢失虚函数特性。
.
仅仅是a的情况,成功的利用了虚函数的徐特性,实现了多态;
3.构造函数不能是虚函数,但是析构函数最好是虚函数,用来正确引导delete析构动态对象。
如果析构函数不是虚函数的情况

如果析构函数是虚函数的情况:

4.虚函数需要注意的地方:
    a. 虚函数在派生类层,界面相同的重载函数都保持函数虚特性;
    b. 虚函数必须是类的成员函数
    c.不能将友元说明为虚函数,但是虚函数可以是另一个类的友元函数。
    d.析构函数可以是虚函数,但是构造函数不可以是虚函数。

三,纯虚函数
纯虚函数是在基类中说明的虚函数,在基类中没有定义,要求任何派生类都定义自己的版本。
它为任何的派生类都提供一个公共的界面,说明形式为
virtual 类型 函数名(参数表) = 0 一个具有虚函数的基类叫做抽象类。
1. 注意事项:
 a.抽象类不能定义对象: base temp;
   
b.可以定义指针。 base *temp; 
   
c.不能作为函数的返回类型  base f();
   
 
d. 不能作为函数的参数  void (base)
e. 可以作为抽象类的引用  base &h( base &)
2.派生类必须要实现纯虚函数的定义才能定义类对象,否则还是个抽象类,如果确实不用定义或者不知道该如何定义,则必须将它定义为空。
  
四、虚函数的应用
 1.应用场景:
    a.虚函数,当我们需要派生类实现多态的功能的时候要利用虚函数的特性
    b.纯虚函数:适合于实现分层结构的软件系统,便于对问题抽象时定义共性,实现时定义区别!!!


五,虚函数表
 普通函数多会映射到特地呢的代码,无论编译阶段还是连接阶段,编译器都能计算出这个函数的地址,调用即可。
虚函数被调用的函数不仅依据调用的特定函数,还依据调用的对象的种类。
问题1:为什么虚函数能够通过父类指针指向子类的时候实现多态?
答:通过虚函数表V-Table

问题2:什么事虚函数表?
答:Virtual Table就是编译期间建立,执行期间查表执行。
它是一个函数指针表,每一个表项都指向一个函数。任何一个包含至少一个虚函数的类都会有这样一张表。需要注意的是Virtual Table只包含虚函数的指针,没有函数体。实现上是一个函数指针的数组。虚函数表解决了继承、覆盖的问题,保证其容真实反应实际的函数。每个派生类的Virtual Table继承了它各个基类的Virtual Table,如果基类Virtual Table中包含某一项,则其派生类的Virtual Table中也将包含同样的一项,但是两项的值可能不同。如果派生类覆盖(override)了该项对应的虚函数,则派生类Virtual Table的该项指向覆盖后的虚函数,没有覆盖的话,则沿用基类的值。

问题3:虚函数表如何寻址?
        每一个类只有唯一的一个Virtual Table,不是每个对象都有一个Virtual Table,恰恰是每个同一个类的对象都有一个指针,这个指针指向该类的Virtual Table(当然,前提是这个类包含虚函数)。那么,每个对象只额外增加了一个指针的大小,一般说来是4字节。
对于单继承:示意图
    
有两种情况:(1)无覆盖
(2)虚函数被覆盖

对(2) 程序说明:
#include <iostream>
using namespace std;
class base
{
public:
        virtual void f(void){cout << "f in base1" << endl;}
        virtual void g(void){cout << "g in base1" << endl;}
        virtual void h(void){cout << "h in base1" << endl;}
};
class Dervied : public base
{
public:
        virtual void f(void){cout << "f in derive" << endl;}
        virtual void g1(void){cout << "g1 in derive" << endl;}
        virtual void h1(void){cout << "h1 in derive" << endl;}
};
typedef void (*Fun)(void);

int main(void)
{
        Fun pFun;
        derive d1;
        pFun = (Fun)*(int*)*(int*)(&d1);
        cout<<"虚函数表的地址:";
        cout<<(int *)*(int *)(&d1)<<endl;
        cout<<"虚函数第一个函数坑地址:";
        cout<<(int*)*(int *)(&d1)<<endl;
        cout<<"虚函数第一个函数的地址:";
        cout<<*(int*)*(int *)(&d1)<<endl;
        pFun();

        cout<<"虚函数第二个函数坑地址:";
        cout<<((int *)*(int*)(&d1) + 1)<<endl;
        cout<<"虚函数第二个函数的地址:";
        cout<<*((int *)*(int*)(&d1) + 1)<<endl;
        pFun = (Fun)*((int *)*((int*)(&d1)) + 1);
        pFun();

        cout<<"虚函数第三个函数坑地址:";
        cout<<((int *)*(int*)(&d1) + 2)<<endl;
        cout<<"虚函数第三个函数的地址:";
        cout<<*((int *)*(int*)(&d1) + 2)<<endl;
        pFun = (Fun)*((int *)*((int*)(&d1)) + 2);
        pFun();
        cout<<"虚函数第四个函数坑地址:";
        cout<<((int *)*(int*)(&d1) + 3)<<endl;
        cout<<"虚函数第四个函数的地址:";
        cout<<*((int *)*(int*)(&d1) + 3)<<endl;
        pFun = (Fun)*((int *)*((int*)(&d1)) + 3);
        pFun();
        return 0;
}
    

多重继承的情况:覆盖的情况和单继承基本一样,就每个虚函数表的对应的函数都会被覆盖。
程序说明:
#include<iostream>
using namespace std;
using namespace std;
class Base1 {
    public:
        //虚函数定义
        virtual void f() { cout << "Base1::f" << endl; }
        virtual void g() { cout << "Base1::g" << endl; }
        virtual void h() { cout << "Base1::h" << endl; }
};
class Base2 {
    public:
        //虚函数定义
        virtual void f() { cout << "Base2::f" << endl; }
        virtual void g() { cout << "Base2::g" << endl; }
        virtual void h() { cout << "Base2::h" << endl; }
};
class Base3 {
    public:
        //虚函数定义
        virtual void f() { cout << "Base3::f" << endl; }
        virtual void g() { cout << "Base3::g" << endl; }
        virtual void h() { cout << "Base3::h" << endl; }
};
//多继承时的情况---无虚函数覆盖
class DerivedMulti:public Base1,public Base2,public Base3{
public:
    //虚函数定义
    virtual void f1() { cout << "DerivedMulti::f1" << endl; }
    virtual void g1() { cout << "DerivedMulti::g1" << endl; }
};
int main()
{
    typedef void(*Fun)(void);
     cout << "多重继承时的情况(无虚函数覆盖):" << endl;
    DerivedMulti dMultiObj;
    cout << "DerivedMulti类第一个虚函数表地址:" << (int*)(&dMultiObj) << endl;
    cout << "依次调用三个虚函数表中的虚函数:" << endl;
    cout << "第一个虚函数表中的虚函数:" << endl;
    Fun pFun = NULL;
    pFun = (Fun)*((int *)*((int*)(&dMultiObj)));
    pFun();
    pFun = (Fun)*((int *)*((int*)(&dMultiObj)) + 1);
    pFun();
    pFun = (Fun)*((int *)*((int*)(&dMultiObj)) + 2);
    pFun();
    pFun = (Fun)*((int *)*((int*)(&dMultiObj)) + 3);
    pFun();
    pFun = (Fun)*((int *)*((int*)(&dMultiObj)) + 4);
    pFun();
    cout << "第二个虚函数表中的虚函数:" << endl;
    pFun = (Fun)*((int *)*((int*)(&dMultiObj)+ 1));
    pFun();
    pFun = (Fun)*((int *)*((int*)(&dMultiObj)+ 1) + 1);
    pFun();
    pFun = (Fun)*((int *)*((int*)(&dMultiObj)+ 1) + 2);
    pFun();
    cout << "第三个虚函数表中的虚函数:" << endl;
    pFun = (Fun)*((int *)*((int*)(&dMultiObj )+ 2));
    pFun();
    pFun = (Fun)*((int *)*((int*)(&dMultiObj )+ 2) + 1);
    pFun();
    pFun = (Fun)*((int *)*((int*)(&dMultiObj )+ 2) + 2);
    pFun();
    return 0;
}


备注:这是在32为操作系统下的测试情况,如果在64位的情况下,指针占用了8个字节,如果我们把指针强制转换成(int *)的时候需要每次加 2 ,或者我们可以把指针强制转换成(long *)










0 0
原创粉丝点击