您的位置:首页 > 编程语言 > C语言/C++

多态专题

2017-05-25 19:43 267 查看
   类的多态性又称为动态联编特性,是指在运行时由实际的对象决定调用基类还是派生类的同名函数。具体来说就是,当基类的指针指向基类或派生类的对象时,不同的对象调用的是不同的同名函数成员。基类对基类对象或派生类对象的引用在调用同名函数时,由实际对象决定调用哪一个同名成员函数。

多态性的特点:

(1)多态性就是实际调用的成员函数由实际对象类型决定,什么类型对象就调用什么类型下的成员函数。

(2)指针或者引用使用对象时,若所有的成员函数是指针或者引用类型的虚函数,则具有多态性

(3)对象调用成员函数是由对象类型决定,不体现多态性。

(4)虚函数不改变重名隐藏规则。

注:类定义时只要带有虚函数(包括继承的虚函数),就会有一个包含该类所有虚函数的函数指针表,这就是该类的虚函数表。

带虚函数的对象的构造过程如下:

(1)按照定义次序依次构造虚基类部分

(2)按定义依次构造普通基类部分。

(3)按定义次序依次构造派生类的扩展部分。

(4)设置虚函数表指针项(指向该类虚函数表的指针)。

(5)执行构造函数体;

虚函数的内部实现原理:

#include<iostream>
using namespace std;
class A
{
protected:
int x;
public:
A(int ix=0):x(ix){}
virtual void func(){cout<<"A::func()<<";}
virtual void show(){cout <<"x="<<x<<endl;}
};
class B:public A
{
private:
int y;
public:
B(int ix =0,int iy =0):A(ix),y(iy){}
void show(){cout<<"x="<<x<<",y="<<y<<endl;}
virtual void fun(){cout<<"B::fun()"<<endl;}
};
int main()
{
A a(123),*pa;
B b(100,200);
pa = &a;
pa->show();
pa = &b;
pa->show();
return 0;
}(1)在以上程序中,由于没有基类,故直接构造数据部分x,然后设置A类虚函数表指针项,最后执行A的构造函数体。
(2)当构造B类对象时,先构造A类部分,构造A类部分如a的构造,此时构造成的A类部分的虚表指针指向A::_vftable。近一步构造扩展部分y,再设置虚函数表指针项使之指向B::_vftable。

(3)当pa指针指向对象a时,Pa调用虚函数show()是通过A类对象的虚函数表指针项在指向的虚函数表中调用函数指针A::show()来实现的。当pa指向对象b时,pa调用虚函数show()任然是通过A类部分的虚函数指针项在指定的虚函数调用函数指针B::show()来实现的。由于基类部分的虚函数表指针项指向了对应于对象的不同的虚函数表,故能够实现对象对应的虚函数。

注:A类对象和A类部分是不一样的概念;在B对象里面的a是A类部分,在a对象里面的是a类对象。



一、C++多态性

 多态是指同一件事情在不同情况下的多种状态;

C++的多态性是指相同的运算符或者函数名可以有多种实现代码方便在不同情况下使用,也就是说一种接口,却能实现不同的方法,就像使用了不同的函数,减少了函数名的使用。

 C++的多态性分为编译时的多态和运行时的多态;

  编译时的多态是指源程序在编译的时候,编译器可以从多种相同函数名或者是运算符函数中选取其中一个链接到源程序的可执行程序中。

  运行时的多态:在编译的时候不能决定到底该执行哪一段代码,只能在程序运行起来的时候才能确定到底在调用哪个版本的代码,这种情况的多态性是使用继承和虚函数完成的,因为是在程序执行的时候确定的,这种编译方式叫做动态联编(动态绑定,晚期绑定)

问题1:为什么在基类的成员函数前加一个“virtual”,将子类对象的地址赋给父类指针时,父类指针就可以调用子类对象的同名成员函数?

答:"virtual"函数支持动态联编(在程序执行的时候它会看看到底是哪个对象在调用该虚函数),把子类对象的地址赋给父类指针,它通过多态联编就知道真正调用该虚函数的是子类对象,所以就会调用子类方法;

二、虚函数的层次性

如果基类函数被声明为虚函数,它可以被派生重新定义,但是并不是一定要在派生类中定义函数,如果在派生类未对该虚函数重新定义,当使用派生类的对象调用该函数时调用的是基类函数。

三、析构函数 与 虚函数

注:构造函数不能申明为虚函数
析构函数可以申明为虚函数,如果在基类中声明了析构函数是虚函数,则在派生类中的析构函数也就自动成了虚函数。当使用delete删除一个对象时,delete隐含的调用了对象的析构函数,因为虚函数支持动态联编,所以他在删除一个对象的时候,会看看是谁在调用;
以下是测试代码:
class Base

{

public:

Base(){}

virtual ~Base(){

cout<<"::~Base()"<<endl;

}

};

class Derived:public Base

{

public:

Derived(){}

~Derived()

{

cout<<"::~Drevied()"<<endl;

}

};

void main()

{

Base *pop_b = new Derived;

delete pop_b ;

}

结果:~Derived();~Base();如果未将析构函数声明成虚函数的话就会出现调用~Base();
三、为什么inline函数,静态函数,构造函数不能是virtual函数?
1、内联函数是在编译的时候展开的,而虚函数是在运行时候动态联编的,所以俩者矛盾,不能定义内联函数为虚函数。
2、构造函数是构造一个对象,但是虚函数的运行是建立在对象的基础上的,在构造函数执行的时候对象尚未形成,所以不能将构造函数声明为虚函数。

3、静态成员函数不属于任何对象,任何实列,它是属于整个类的,virtual是建立在对象的基础上的,所以对于静态函数来说是没有任何意义的。
4、静态成员和非静态成员的区别是,静态成员没有this指针。虚函数是依靠Vptr和Vtable,在类的构造函数中创建生成,并且只能通过this指针指向他。 虚函数的调用关系:this -> vptr -> vtable ->virtual function

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息