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

2017-03-02 10:12:08来源:http://blog.csdn.net/qcyfred/article/details/53446940作者:qcyfred人点击

引言

多态,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 
#include
using namespace std;class CAnimal
{
public:
CAnimal();
virtual ~CAnimal();
virtual void showName() const{
cout<<"动物:我不知道我是谁"< }
virtual void say() const{
cout<<"动物:我不知道怎么叫"< }
virtual void eat() const{
cout<<"动物:我不知道吃什么"< }
protected:
string name;
private:
};
CAnimal::CAnimal()
{
}
CAnimal::~CAnimal()
{
}class CCat : public CAnimal
{
public:
CCat();
~CCat();
void showName() const{
cout<<"动物:我是猫"< }
void say() const{
cout<<"动物:喵喵喵"< }
void eat() const{
cout<<"动物:我吃鱼"< }private:
};
CCat::CCat()
{
}
CCat::~CCat()
{
}
class CDog : public CAnimal
{
public:
CDog();
~CDog();
void showName() const{
cout<<"动物:我是狗"< }
void say() const{
cout<<"动物:汪汪汪"< }
void eat() const{
cout<<"动物:我啃骨头"< }
private:
};
CDog::CDog()
{
}
CDog::~CDog()
{
}
class CFood
{
public:
CFood();
virtual ~CFood();
virtual void showName() const{
cout<<"食物:我不知道我是什么食物"< }
private:
};
CFood::CFood()
{
}
CFood::~CFood()
{
}
class CFish : public CFood
{
public:
CFish();
~CFish();
void showName() const{
cout<<"食物:我是鱼"< }
private:
};
CFish::CFish()
{
}
CFish::~CFish()
{
}
class CBone : public CFood
{
public:
CBone();
~CBone();
void showName() const{
cout<<"食物:我是骨头"< }
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 OOPand supports
– function overloading and operatoroverloadingatcompile time(编译时多态)
– functionoverridingatrun-timeassociate many meanings to one function(运行时多态)

Run-time polymorphism
– enable programmers to design a commoninterface that can be used on different butrelated objects
– reduce complexity and development time
Compile-time polymorphism
– apply static binding
– advantage of fast speed
– realized by function overloading andoperator overloadingRun-time polymorphism
– apply dynamic binding
– advantage of enhanced flexibility
– realized by inheritance + virtual functions
In C++, redefining a virtual function in aderived 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 thederived class(互相转型,3种情况!
– can assign a derived-class object to a base-class object // 1. 在assign的时候,可以向上转型!
– can copy the address of a derived-classobject to a pointer of a base-class object // 2. 可以把派生类的地址assign给指向基类的指针
– a derived-class object can be a referenceto base-class object // 3. 可以写 B0 & b0 = D1;
But! can only access members in the baseclass, not members in the derived class (都只能范围基类中的成员!不能访问派生类中的成员!

虚函数

Virtual functions makes that
a pointer orreference
of a base-class object can beapplied 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 memberfunction of a class  cannot be global,static or friend.
Destructors can be virtual but constructorscannot be virtual.Major disadvantage: more storage overhead +running slower
#include 
#include
#include
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 list = new ArrayList ();
如果用子类去声明的话,有可能会发生点不出来父类中写过的一些函数的情况。
e.g. overloaded members in baseclasses cannot be accessed directly.
上一篇也提到过。
Review:C++多继承
#include 
using namespace std;class CB0
{
public:
CB0() {};
~CB0() {};
void fun() {
cout<<"CB0::fun()"< }
void fun(int i) {
cout<<"CB0::fun(int i) "< }
};
class CD0 : public CB0
{
public:
CD0() {};
~CD0() {};
void fun(int i) {
cout<<"CD0::fun(int i) "< }
};
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 
using namespace std;class CB0
{
public:
CB0() {};
virtual ~CB0() {};
virtual void fun() {
cout<<"CB0::fun()"< }
virtual void fun(int i) {
cout<<"CB0::fun(int i) "< }

private:
};
class CD0 : public CB0
{
public:
CD0() {};
~CD0() {};
virtual void fun(int i) {
cout<<"CD0::fun(int i) "< }
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 BfpB->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 itsown version
Class with one or more pure virtualfunctions is abstract base class(注意,是抽象基类,不是虚基类。虚基类是虚拟继承里的概念!)
– can only be used as base class
– no objects can ever be created from it
because it doesn’t include completedefinitions of all its members
抽象基类不能实例化!
If one derived class fails to define all purevirtual functions,
– also an abstract base class
如果抽象基类的派生类,没有重新定义所有的虚函数,那这个派生类依然是抽象基类,还是不能被实例化!
纯虚函数的声明方式。后面等于0,不要写函数体。
virtual void Area()=0; //pure virtual!

最新文章

123

最新摄影

微信扫一扫

第七城市微信公众平台