C++(14):OOP之核心——多态(Polymorphsim)

    xiaoxiao2021-12-14  17

    引言

    多态,Polymorphsim,多种状态,面向对象程序设计的核心。

    据说,这是OOP程序员在应聘的时候,第一大考点!!!!!!

    就我目前的认知水平来看,多态的出现,是为了在庞大的软件工程里,降低开发的复杂度。

    e.g. 一个软件工程量很大,通过定义一些接口,interface,不同的人按照这些接口所指定的规则(输入、输出),

    去实现具体的内容。最后,软件统一整合的时候,才能对接得上,相当于是协作关系。

    不过我觉得吧,定义一堆函数,不也就可以吗?找xxx去实现函数a,找yyy去实现函数b。这样看起来,似乎也可以啊……

    难道这成了面向过程的了?难道这样不能大家一起合作开发吗?

    暂时还是没有太能理解这种设计思想……哎…………【注意】后面一个案例,就可以体现。没有多态,开发起来很麻烦!

    按照百度百科的说法,在OOP中,接口的不同实现方式,就叫做多态。

    如果某种程序设计语言,有对象,但是不支持多态,那么只能叫基于对象(object based),不能叫面向对象(object oriented)。

    现在也没太能理解这种意思。

    反正我现在的理解就是,运用多态,似乎可以更好地让一群人分工合作,协同开发。类似于自己制定的一套规范,标准。还有,解耦合?没懂……

    就像当时看一些JAVA框架的时候,e.g. struts,就相当于制定了一套规范。e.g. MVC,每一个view(jsp)的下一步一定是调到一个controller(java)去…

    这样,好像就是行业标准吧。行业标准似乎就是为了实现大统一,只要我们都符合这个标准,只要都按照这种规范去开发,你能用我的,我也能用你的。

    就像用USB线给手机充电一样。普通USB数据线,不管是三星,还是小米,还是华为,只要这些手机的充电口都统一符合“采用某种USB接口研发、生产制造”的标准,那这些手机的充电线是可以通用的。至于手机里面怎么充电的,是什么电池,容量多少,好像并不关心。手机充电头那边是怎么把220V转成5V,1A的输出,也无所谓。只在乎“你有android手机的充电线吗?”这句话。

    再举一个“多态”的例子。

    我听别人说的。

    一位老板,一声令下,“走吧!”

    司机,开左前车门,从左前车门上车,发动汽车。

    保镖,开右后车门,请老板上车。待老板上车后,自己开右前车门,上车。

    老板,从右后车门,上车。

    秘书,开左后车门,从左后车门上车。

    尽管,同一个“函数”也好、“命令”也好,但是,不同的角色,不同的人,却有不同的动作和响应。这就是多态。

    而不是需要老板说,司机,你干嘛干嘛,保镖,你干嘛干嘛,小秘,你又干嘛干嘛。这样累不累?

    【经典案例】

    清华的某位教授说,什么叫经典例题?

    1. 本来就很经典;

    2. 每次做,都会有新的收获!

    #include <iostream> #include <string> using namespace std; class CAnimal { public: CAnimal(); virtual ~CAnimal(); virtual void showName() const{ cout<<"动物:我不知道我是谁"<<endl; } virtual void say() const{ cout<<"动物:我不知道怎么叫"<<endl; } virtual void eat() const{ cout<<"动物:我不知道吃什么"<<endl; } protected: string name; private: }; CAnimal::CAnimal() { } CAnimal::~CAnimal() { } class CCat : public CAnimal { public: CCat(); ~CCat(); void showName() const{ cout<<"动物:我是猫"<<endl; } void say() const{ cout<<"动物:喵喵喵"<<endl; } void eat() const{ cout<<"动物:我吃鱼"<<endl; } private: }; CCat::CCat() { } CCat::~CCat() { } class CDog : public CAnimal { public: CDog(); ~CDog(); void showName() const{ cout<<"动物:我是狗"<<endl; } void say() const{ cout<<"动物:汪汪汪"<<endl; } void eat() const{ cout<<"动物:我啃骨头"<<endl; } private: }; CDog::CDog() { } CDog::~CDog() { } class CFood { public: CFood(); virtual ~CFood(); virtual void showName() const{ cout<<"食物:我不知道我是什么食物"<<endl; } private: }; CFood::CFood() { } CFood::~CFood() { } class CFish : public CFood { public: CFish(); ~CFish(); void showName() const{ cout<<"食物:我是鱼"<<endl; } private: }; CFish::CFish() { } CFish::~CFish() { } class CBone : public CFood { public: CBone(); ~CBone(); void showName() const{ cout<<"食物:我是骨头"<<endl; } private: }; CBone::CBone() { } CBone::~CBone() { } class CMaster { public: CMaster(); ~CMaster(); void feed(CAnimal & an, CFood & food) const{ an.showName(); an.say(); food.showName(); an.eat(); } private: }; CMaster::CMaster() { } CMaster::~CMaster() { } int main () { CAnimal * an = new CCat; CFood * food = new CFish; CMaster * master = new CMaster; master->feed(*an,*food); delete an; delete food; delete master; system("pause"); return 0; }动物:我是猫 动物:喵喵喵 食物:我是鱼 动物:我吃鱼

    主人类中,feed函数,本来按理说就应该填一个动物,填一种食物,就好。

    多态提供了足够的灵活性!

    主人类中,代码可以写得很少啊!!!(有助于开发,特别是有助于减少架构师的工作量…)

    如果没有多态,主人类中,要重载至少2个函数嘛。

    一个给猫喂鱼,一个给狗喂骨头。

    要是有更多的子类,主人类的开发要被写死!而且,维护起来很困难啊!

    多态概述

    据说,多态的形式分2种。我直接从PPT上摘下来。 Polymorphism is one of three keys in OOP and supports – function overloading and operator  overloading at  compile time(编译时多态) – function  overriding at  run-time associate many meanings to one function (运行时多态) Run-time polymorphism – enable programmers to design a common interface that can be used on different but related objects – reduce complexity and development time Compile-time polymorphism – apply static binding – advantage of fast speed – realized by function overloading and operator overloading Run-time polymorphism – apply dynamic binding – advantage of enhanced flexibility – realized by inheritance + virtual functions In C++, redefining a virtual function in a derived class is called overriding a function.

    绑定的方式

    静态绑定,static binding, early binding,在编译时进行绑定 at compile time. 动态绑定,dynamic binding, late binding,在运行时进行绑定 at run-time. 在JAVA里面,多态是可以通过写一个interface,然后某个class去implement这个接口,再override接口里的函数,实现。 好像也可以通过直接用class A 去 extends class B,并override B中的函数,实现。 在C++里,使用abstract base class和virtual function实现。 如果A是一个class, A * a = new A; a.fun1(); 属于静态绑定,编译时就知道要调用A的fun1()函数了。 如果A是一个class,A中的fun1()前面用了virtual修饰词, B又继承了A,如果在B中override了fun1()这个函数, A * a = new B; a.fun1(); 属于动态绑定,只有到运行时才知道,虽然是a.fun1();,但其实是要调用B的fun1()函数。 我暂时不明白,为什么只有到运行时,才会知道…… 难道在编译的时候,编译器看不懂我写的代码? (明明new的就是B啊)?难道只有在运行时,才会发现new的是B? 这是什么情况…… 有一种官方的说法。 Casting between the base class and the derived class( 互相转型,3种情况!) – can assign a derived-class object to a  base-class object // 1. 在assign的时候,可以向上转型! – can copy the address of a derived-class object to a pointer of a base-class object // 2. 可以把派生类的地址assign给指向基类的指针 – a derived-class object can be a reference to base-class object // 3. 可以写 B0 & b0 = D1;  But! can only access members in the base class, not members in the derived class ( 都只能范围基类中的成员!不能访问派生类中的成员!

    虚函数

    Virtual functions makes that a pointer or reference of a base-class object can be applied onto a derived-class object(注意,也 只能是虚函数加上pointer or ref才会体现,assign也是不行的!Virtual functions tell the compiler – don’t know how function is implemented – wait until used in program – get implementation from object instance – call dynamic (late) binding (只要写了virtual function,编译器自己就会去等,直到运行时…) If a function f() in the base class is virtual, then all f()‘s in the derived classes are virtual. (只要父类中f是虚函数,即用virtual声明了,则之后在它的所有派生类中,f这个函数都是虚函数,即时没有用virtual声明) A virtual function must be a member function of a class cannot be global, static or friend. Destructors can be virtual but constructors cannot be virtual. Major disadvantage: more storage overhead + running slower #include <iostream> #include <cstdlib> #include <cstring> using namespace std; class B0 { public: void ShowFun() { //not virtual cout << "B0::ShowFun()" << endl; } }; class C0 : public B0 { public: virtual void ShowFun() { //virtual cout << "C0::ShowFun()" << endl; } }; class C1 : public C0 { public: void ShowFun() { //virtual cout << "C1::ShowFun()" << endl; } }; class C2 : public C1 { public: void ShowFun() { //virtual cout << "C2::ShowFun()" << endl; } }; void FunPtr(C0 *ptr) { ptr->ShowFun(); } int main() { B0 w, *p; C0 x, *q; C1 y; C2 z; p = &w; p->ShowFun(); p = &y; p->ShowFun(); //Q1: what happen?? q = &x; FunPtr(q); //Q2: which to call? q = &y; FunPtr(q); q = &z; FunPtr(q); //q = &w; FunPtr(q); //Q3: what happen?? //FunPtr(&w); //Q4: what happen?? => cannot covert from B0* to C0*; but the reverse can //FunPtr(p); //Q5: what happen?? => cannot covert from B0 to C0* ; but the reverse can system("pause"); return 0; } 结果。 B0::ShowFun()  // p是指向B0的指针,输出是B0 B0::ShowFun()  // 不是虚函数,不会访问派生类的成员 C0::ShowFun()  // 虚函数。 showFun在B0里已经是虚函数了。在子类中也会是虚函数!尽管override了! C1::ShowFun()  //  C2::ShowFun() 【注意】 1. 如果某个类是基类,立即把它的析构函数写成虚函数! 【经典案例】 class A { public: ~A() { cout << “A::~A()\n”;} }; class C : public A { int * iary; public: C(int i) { iary = new int [i]; } ~C() { delete [] iary; cout << “C::~C()\n”; } }; //in main() A *pa = new C(10); delete pa; 派生类对象开辟的内存,并没有回收!!! 所以,一定要把析构函数写成虚函数!!! 2. 尽量用父类去声明,尽管最后new出来的是子类! 和java里的写法一样。 List <Object> list = new ArrayList<Object> (); 如果用子类去声明的话,有可能会发生点不出来父类中写过的一些函数的情况。 e.g.  overloaded members in base classes cannot be accessed directly. 上一篇也提到过。 Review: C++多继承 #include <iostream> using namespace std; class CB0 { public: CB0() {}; ~CB0() {}; void fun() { cout<<"CB0::fun()"<<endl; } void fun(int i) { cout<<"CB0::fun(int i) "<<endl; } }; class CD0 : public CB0 { public: CD0() {}; ~CD0() {}; void fun(int i) { cout<<"CD0::fun(int i) "<<endl; } }; int main () { CB0 * obj1 = new CD0; // 父类声明,却有两个fun obj1->fun(); obj1->fun(0); CD0 obj2; obj2.fun(0); // 子类声明,只有这一个了! system("pause"); return 0; } 结果。 CB0::fun() CB0::fun(int i) CD0::fun(int i) 因为没写虚函数,所以指针的值虽然保存的一个CD0对象的地址,但通过这个指针,fun也只能调用CB0的fun了。 如果用了虚函数 #include <iostream> using namespace std; class CB0 { public: CB0() {}; virtual ~CB0() {}; virtual void fun() { cout<<"CB0::fun()"<<endl; } virtual void fun(int i) { cout<<"CB0::fun(int i) "<<endl; } private: }; class CD0 : public CB0 { public: CD0() {}; ~CD0() {}; virtual void fun(int i) { cout<<"CD0::fun(int i) "<<endl; } private: }; int main () { CB0 * obj1 = new CD0; // 父类声明,却有两个fun obj1->fun(); obj1->fun(0); CD0 d0; d0.fun(1); // 子类声明,只有一个fun。在子类中,override过什么,同名的就只有什么了。 system("pause"); return 0; } 结果。 CB0::fun() // 这确实在调用父类的fun。 CD0::fun(int i) // 由于多态,调用子类的fun。一定要是整个函数一模一样,才算覆写!覆写≠重载! CD0::fun(int i) // 本来就是用CD0声明的,所以肯定调用子类的。 【经典案例】吐血推荐! class B { public: void f() { cout << “Bf ”; } virtual void g(){ cout << “Bg ”; } void h() { g(); f(); } virtual void m(){ g(); f(); } }; class D : public B { public: void f() { cout << “Df ”; } void g() { cout << “Dg ”; } void h() { f(); g(); } void m(int i) {f(); g();} }; //in main() D d; B *pB = &d; pB->f(); pB->g(); pB->h(); pB->m();结果会是: Bf Dg Dg Bf Dg Bf pB->h(); h在B里不是虚函数,所以调用B的h()。 B的h里面,有g,而g是虚函数,所以调用D的g,输出Dg, B的h里面,还有f,f不是虚函数,所以调用B的f,输出Bf。 同理,pB->m(); m()是在B里虚函数,但是D中没有override( override一定要是两个函数一模一样!!!),只是又增加了一个void m(int i)的函数 所以调用B中的m, B的m里面,有g,g是虚函数,调用D的g,输出Dg, B的m里面,还有f,f不是虚函数,调用B的f,输出Bf game over…

    纯虚函数

    (类似于Java里面的interface) Pure virtual functions require no definition  – force each derived classes to define its own version Class with one or more pure virtual functions is abstract base class(注意,是抽象基类,不是虚基类。虚基类是虚拟继承里的概念!) – can only be used as base class – no objects can ever be created from it because it doesn’t include complete definitions of all its members 抽象基类不能实例化! If one derived class fails to define all pure virtual functions, – also an abstract base class 如果抽象基类的派生类,没有重新定义所有的虚函数,那这个派生类依然是抽象基类,还是不能被实例化! 纯虚函数的声明方式。后面等于0,不要写函数体。 virtual void Area()=0; //pure virtual!
    转载请注明原文地址: https://ju.6miu.com/read-970413.html

    最新回复(0)