C++ 多态解析

来源:互联网 发布:qq聊天记录查看软件 编辑:程序博客网 时间:2024/06/07 18:08

C++的多态性 

今天我们,来将将C++的三大特性之一的——————  多态性》》》为什么会有这个呢????、
因为,我们编写代码时,有的时候,当对象不同时,就需要调用不同的函数。。。在这个时候就需要使用的多态了。
再说类的多态性之前我先来说说对象这个概念。。。

对象的类型

对象的概念可以分为两种类型

所谓的静态类型,就是一般的类型 。。。
而动态类型,举个例子来说就像是赋值兼容规则里说的
       一个基类对象可以用 派生类对象来赋值;在这里的这个对象就是动态类型的。
因为只有在运行时调用对象时  才知道调用的实际上是一个派生类的对象。。。

多态

多态概念:多态这一特性,用通俗的话来说就是  一个事物在不同的时候表现为多种状态。。。。
但是在C++中多态有这更为广泛的含义


静态多态

所谓静态多态,指的是编译器在编译期间完成的,编译器根据函数实参的类型(可能会进行隐式类型转换),可推
断出要调用那个函数,如果有对应的函数就调用该函数,否则出现编译错误。
函数重载,编译器在编译时,就已经判断出参数的类型 ,根据类型的不同来调用不同的函数。。。。
模板 ,也如函数重载一般,根据模板参数的不同调用函数。。。。
函数重载例子::::



关于模板的示例可以看看我之前写的C++函数模板

动态多态

动态绑定指的是在程序执行期间(非编译期)判断所引用对象的实际类型,根据其实际类型调用相应的方法。


在这里我们动态多态讲的主要是  说     虚函数

虚函数 

使用virtual关键字修饰类的成员函数时,指明该函数为虚函数,派生类需要重新实现,编译器将实
现动态绑定。

下面来举个虚函数的示例:
class B{public: virtual void Base(){cout << "void B::Base()" << endl;}};class D:public B{public:void Base(){cout << "void D::Base()" << endl;}};int main(){B b;D d;B* b1 = &d;b.Base();b1->Base();d.Base();return 0;}
代码执行结果:


从输出的结果上我们看出
b.Base();//调用的是基类的函数b1->Base();//调用的是派生类的函数d.Base();//调用的是派生类的函数
函数调用时b1调用的是对象d内存,所以调用的是派生类的函数;;;;

【动态绑定条件】
1、必须是虚函数
2、通过基类类型的引用或者指针调用虚函数


下面说说在多态中 我们可能会遇到几种函数名相同的情况:
1、函数重载 
限定条件为:
1)在同一作用域内;(一定要记住这一点)
2)函数名相同;
3)参数列表相同(参数的顺序 ,类型 ,长度)
2、同名隐藏
限定条件为:
1)一个在基类中,一个在派生类里;
2)函数名相同;
3、函数重写
限定条件为:
1)一个在基类中,一个在派生类里
    2)都是虚函数;
3)函数原型相同(函数名,参数,返回值相同)
4、协变
限定条件为:
1)一个在基类中,一个在派生类里
2)都是虚函数
3)函数名相同,参数列表相同
4)函数返回值分别为基类的指针(引用),  派生类类的指针(引用)


下面有一段代码我们来看看
class B{public:virtualvoid Test1(int _test){ cout << "B::Test1()" << endl; }void Test2(int _test){ cout << "B::Test2()" << endl; }void Test3(int _test){ cout << "B::Test3()" << endl; }virtualB* Test4(int _test1, int _test2){cout << "B::Test4()" << endl; return  this;}};   class D :public B{public:virtualvoid Test1(int _test){ cout << "D::Test1()" << endl; }virtualvoid Test2(int _test){ cout << "D::Test2()" << endl; }void Test3(int _test){ cout << "D::Test3()" << endl; }virtualD* Test4(int _test1, int _test2){cout << "D::Test4()" << endl;return this;}};

上述代码中
Test1()属于函数重写
Test2()属于虚函数
Test3()属于同名隐藏
Test4()属于协变

纯虚函数

所谓纯虚函数
在成员函数的形参后面写上=0,则成员函数为纯虚函数。
包含纯虚函数的类叫做抽象类(也叫接口类),抽象类不能实例化出对象。纯虚函数在派生类中重新定义以后,派生类才能实例化出对象。
用代码来直观说明一下吧
class Base{public:virtual  void Test() = 0;//纯虚函数int _b;};class Derived :public Base{};

在这段代码中,Base就是一个抽象类,而Test函数就是一个纯虚函数
所以,不能使用Base类 来定义一个对象 。


那些函数可以定义为虚函数呢?????
  
总结一句话就是      只有类的非静态成员函数才能定义为虚函数

但是类的构造函数不能定义为虚函数,因为构造函数是在类对象创建时调用的,
而虚函数是通过基类的指针和派生类的指针和引用调用的,
如果将构造函数定义为虚函数,无法正常实现虚函数与构造函数的功能。


一般情况下,要将基类析构函数定义为虚函数,为什么呢???
class Base{public:Base();virtual  ~Base();};class Derived :public Base{public:Derived();~Derived();};
当声明一个基类的引用 ,用派生类赋值
int mian(){Derived  d;Base &rb = d;return 0;}

当函数结束后 ,rb类析构时,应该调用的派生类的析构函数,所以应该将基类的析构函数定义为虚函数,
来根据参数的不同调用不同的析构函数;

至于为什么只有非静态的成员函数才能定义 成虚函数??
我还没搞清楚》》》之后再研究研究




下面是对上面所说的虚函数编写时所要注意的地方(对上面的总结)
1、派生类重写基类的虚函数实现多态,要求函数名、参数列表、返回值完全相同。(协变除外)
2、基类中定义了虚函数,在派生类中该函数始终保持虚函数的特性。(就像是上面说的函数Test2)
3、关于哪些函数可以定义为虚函数,只有类的非静态成员函数才能定义为虚函数,静态成员函数不能定义为虚函数。(构造函数除外 )
4、构造函数不能定义为虚函数,虽然可以将operator=定义为虚函数,但最好不要这么做,使用时容
易混淆
5、如果在类外定义虚函数,只能在声明函数时加virtual关键字,定义时不用加(声明和定义只在一个地方加上virtual)
6、不要在构造函数和析构函数中调用虚函数,在构造函数和析构函数中,对象是不完整的,可能会
出现未定义的行为。(因为虚函数是通过虚表来调用的,构造与析构时,对象不完整)。
7、最好将基类的析构函数声明为虚函数。(析构函数比较特殊,因为派生类的析构函数跟基类的析构
函数名称不一样,但是构成覆盖,这里编译器做了特殊处理)















0 0