引言
多态,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>结果。 B0::ShowFun() // p是指向B0的指针,输出是B0 B0::ShowFun() // 不是虚函数,不会访问派生类的成员
#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;
}
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>结果。CB0::fun()
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(int i)
CD0::fun(int i)
因为没写虚函数,所以指针的值虽然保存的一个CD0对象的地址,但通过这个指针,fun也只能调用CB0的fun了。如果用了虚函数
#include <iostream>结果。CB0::fun() // 这确实在调用父类的fun。
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;
}
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()结果会是: Bf Dg Dg Bf Dg Bf
D d; B *pB = &d;
pB->f(); pB->g(); pB->h(); pB->m();
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!