c++的动态多态

                                 动态多态

再讲虚函数之前,提一下,我在刚开始接触总会混淆虚函数和虚继承。

 总结一下:

a虚继承会产生一个偏移指针指向一个偏移表

定义形式:class B:virtual public A (是继承)

b虚函数会产生一个指针地址指向虚表

定义形式:

class B

{

Public:

Virtual void Fun()(是函数)

{}

}

 

动态多态:在程序执行期间判断引用对象的实际类型,根据其实际类型调用相应的方法,动态多态使用虚函数实现的。

虚函数:所谓的虚函数,就是基类声明的函数时虚拟的然后在派生类中才真正的定义此函数

动态绑定的条件:

1)必须是虚函数,该虚函数被派生类重写

2)通过基类类型的指针或引用调用虚函数

 

我们先来看下虚函数

:用关键字Virtual修饰类的成员函数

public:

virtual void Testfun(int i)

{};

};

class B:public A

{

public:

virtual void Testfun(int i)    //重写基类,这个virtual也可以不写,//但同样继承基类虚函数的特性

{  

cout<<a<<endl;

}

int a;

};

void Test()

{

A a;

B b;

A *p;

p=&b;

p->Testfun(2);

}

 

我们可以惊奇的发现,我用一个基类指针访问了派生类属于派生类自己的函数成员,这就是虚函数

提一下重写:即覆盖

1)在不同的作用域(基类和派生类)

2)函数名,参数,返回值相同(除协变

3)基类函数必须有virtual关键字

4)访问修饰符可以不同(但仍遵循继承的访问原则) 

注意:

1)构造函数不要定义为虚函数,析构函数可以定义为虚函数。

2)不要在构造函数和析构函数内部调用虚函数,因为在构造函数和析构函数中,对象是不完整的。

3)虚函数可以在类内声明,类外定义,在类外定义是不用加virtual。

4)虚表是所有类对象共用的。

 

可见b,b1都是B类型,虚表地址都是0x00875804(共享虚表)

二.下面我们一步一步来分析虚表

(1)对一个简单的虚函数

class A

{

public:

virtual void Testfun()

{     cout<<"A::Testfun 1"<<endl;    }

int i;

};

void Test()

{

   A p;

p.i =7;

}

图1

我们在Test添加以下代码看看这个地址里的是不是该虚函数

typedef void(* PFV)();   //一个返回值为空,参数列表为空的函数类型

PFV *pfv=  (PFV*)*(int *)(&p);

(*pfv)();

结果:


 

 在0x00b6109b里面的是函数Testfun1

 单继承中虚函数

(2.1)没有覆盖的

 

void Test()

{

B p;

p.i =7;

p.b=9;

PFV *pfv=(PFV*)*(int *)(&p);

for(int i=0;i<3;i++)

{

    (*pfv)();

pfv++;

}

}

 

}

来看看P::A的虚表指针里面有什么

         

                          (图1)

可见B的虚函数的地址加在它所继承的A的虚表的后头,相当于把A的虚表 拷贝了一份然后加上自己的那部分。

(2.2)有重写

class B:public A,public C

在上个代码的classB中加入以下代码,重写void Testfun1()

virtual void Testfun1()

{

cout<<"B::Testfun1"<<endl;

}


在测试代码中加如下代码,即打印婆中属于A的部分的虚表

 

A &pv=p;

PFV *pfv=(PFV*)*(int *)(&pv);

for(int i=0;i<4;i++)

{

    (*pfv)();

pfv++;

}

()(图片2)

可见和(图1)的一样,相当于把A的虚表 拷贝了一份然后追加上自己的那部分。并用自己重写的去覆盖与A相同的部分。

3.虚拟的多继承

(3.1)没有覆盖的

class B:public A,public C

在(2.1)的代码加入新类class C

class C

public:

virtual void Testfun4()

{

cout<<"C::Testfun4"<<endl;

}

 int c;

};

在加人打印p中属于C的部分虚表指针所指的内容,打印出p中属于A的虚表指针所指的内容




可见,派生类的自己的虚函数追加到了拷贝自顺序继承的第一位A(class B:public A,public C)的虚表中,属于p的基类C的有自己的虚表(A有自己的虚表)

注意:图2的虚表是属于B类型的p的,与A类自己的虚表并没有关系,是B继承A是拷贝了一份A的虚表,并 或追加或修改


(3.2)有覆盖的多继承

在上个代码的classB中,重写void Testfun1()

virtual void Testfun1()

{

cout<<"B::Testfun1"<<endl;

}

在加人打印p中属于C的部分虚表指针所指的内容,打印出p中属于A的虚表指针所指的内容



 

可见:与3.1相比B类因为重写了Testfun1,所以覆盖了A类的Testfun1,(覆盖可类比2.2)。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值