C++ 中的多态是极为复杂、难缠、庞大的,就像深海里的鲨鱼,凶猛而强大。那么到底如何才能游刃有余地驾于好这头潜伏在深海的远古猛兽呢?!别着急,咱们慢慢来,一点一点地摸索清它的性格和脾气,再控制就容易啦。
例甲:
#include<iostream>
using namespace std;
class TradesPerson {
public:
virtual void sayHi() { cout << "Just Hi, and nothing." << endl; }
};
class Tinker : public TradesPerson {
public:
virtual void sayHi() { cout << "Hi, I'm a Tinker." << endl; }
};
class Tailor : public TradesPerson {
public:
virtual void sayHi() { cout << "Hi, I'm a Tailor." << endl; }
};
int main() {
TradesPerson* p;
int which;
do{
cout << "1 = TradesPerson, 2 = Tinker, 3 = Tailor, 4 = quit; /n ";
_input: cin >> which;
}while( which < 1 || which >4 );
switch( which ) {
case 1: p = new TradesPerson; break;
case 2: p = new Tinker; break;
case 3: p = new Tailor; break;
case 4: exit( 0 );
}
p->sayHi();
goto _input;
delete p;
return 0;
}
模型一:
class TradesPerson {
public:
virtual void sayHi() { cout << "Just Hi, and nothing." << endl; }
};
class Tinker : public TradesPerson {
public:
virtual void sayHi() { cout << "Hi, I'm a Tinker." << endl; }
};
class Tailor : public TradesPerson {
public:
virtual void sayHi() { cout << "Hi, I'm a Tailor." << endl; }
};
模型二:
class TradesPerson {
public:
virtual void sayHi() { cout << "Just Hi, and nothing." << endl; }
};
class Tinker : public TradesPerson {
public:
void sayHi() { cout << "Hi, I'm a Tinker." << endl; }
};
class Tailor : public TradesPerson {
public:
void sayHi() { cout << "Hi, I'm a Tailor." << endl; }
};
模型三:
class TradesPerson {
public:
void sayHi() { cout << "Just Hi, and nothing." << endl; }
};
class Tinker : public TradesPerson {
public:
void sayHi() { cout << "Hi, I'm a Tinker." << endl; }
};
class Tailor : public TradesPerson {
public:
void sayHi() { cout << "Hi, I'm a Tailor." << endl; }
};
模型四:
class TradesPerson {
public:
void sayHi() { cout << "Just Hi, and nothing." << endl; }
};
class Tinker : public TradesPerson {
public:
virtual void sayHi() { cout << "Hi, I'm a Tinker." << endl; }
};
class Tailor : public TradesPerson {
public:
virtual void sayHi() { cout << "Hi, I'm a Tailor." << endl; }
};
实验结果:模型一和模二相等,模三和模四相等。那么也就是说,当声明了基类的一个成员函数为虚后,那么如果该函数在派生类中再次出现并得到新定义,它将自动成为虚函数!可如果在基类没有声明为虚,那么到派生类再怎么声明是虚的都变得毫无意义了——根本起不到多态的神奇妙用。
说到多态,咱们在此再重复一下多态的概念:多态性也称后约束或动态约束,它常用虚函数来实现。在C++中,多态是指C++的代码可以根据运行情况的不同而执行不同的操作。C++的多态性就是为同一个函数和运算符定义几个版本。C++支持两种多态性,编译时的多态性和运行时的多态性。编译时的多态性通过使用重载函数获得,运行时的多态性通过使用继承和虚函数获得。要获得多态性的对象,必须建立一个类等级,然后在派生类中重定义基类函数,该函数可以被定义为重载函数或虚函数,以获得编译时的多态性对象或运行时的多态性对象。
一次非常成功而快乐的多态实验!赶快来先睹为快吧!
例甲:
class A {
public:
void disp() { cout << "This is A speaking!/n"; }
};
class B : public A {
public:
void disp() { cout << "This is B speaking!/n"; }
};
int main() {
A* pa = new B;
pa->disp(); //输出为This is A speaking!
delete pa;
return 0;
}
如果为了让其输出"This is B speaking!/n"的话,你可能会把那个关键句改成pa->B::disp();,可实验证明,这么做编译为报错的。而且显示三个错误,什么“'B' : is not a member of 'A'”、“ 'B' : is not a class or namespace name”等一派胡言乱语!那好,既然让其输出B speaking已经不可能了,接下来就virtual伺候吧!
例乙:
class A {
public:
virtual void disp() { cout << "This is A speaking!/n"; }
};
class B : public A {
public:
void disp() { cout << "This is B speaking!/n"; }
};
int main() {
A* pa = new B;
pa->disp(); //输出为This is B speaking!
delete pa;
return 0;
}
很明显,无非多了个关键字儿virtual,世界竞如此大变!指针pa调用了B类里的成员函数,这也是应该的、应得的、正常的!因为它pa是B类给的它初始化!虽然A类是pa的亲生母亲,但是,是B类一把屎一把尿养活了pa!所以当pa长大成人、能做事能挣钱时,它理应赡养孝顺B类!!这没什么好商量的!!
如果想让其输出This is A speaking!,也可以,直接修改关键句为: pa->A::disp(); 就行了!
对比例甲与例乙,我们看到,当没有用虚函数时,只能输出A speaking,不能企图通过用域解析符::来改变函数调用输出B speaking;但使用了virtual后,代码好像自己长眼睛了,知道程序员心里真正想调用的是谁并把程序员的心声正确付诸于实践,而且函数调用也变得来去自如,想调谁调谁!(欲调其他类的同名成员函数,只需加个域解析符即可!)。
不过也别高兴的太早,这两道例子看起来简单无比,可我硬是花了几个星期地功夫才摸索出来。难的、使我在大门外急得直跺脚就是进不来的东西是:你能否正确实验并探究出虚函数的精要!也就是说,你很容易做出错误的实验!废话少说,来看看一条“不该地实验,不该地爱……”
例甲:
int main() {
B* pb = new A;
pb->disp();
delete pb;
return 0;
}
用派生类B创建指针对象!用基类A给它初始化!结果一编译呢——错!“cannot convert from 'class A *' to 'class B *'”。所以呢,我估且得出一个结论:如果不想跟编译器过不去,就不能用派生类创建对象然后基类初始化。只能是 A* pa = new B; ,谨记谨记!
以下三个例子中,主函均为:
int main() {
A* pa = new B;
pa->disp();
delete pa;
return 0;
}
因而全部省略。
例甲:
class A {
public:
A() { cout << "A constructed,yeah!/n"; }
virtual void disp() { cout << "This is A speaking!/n"; }
};
class B : public A {
public:
B() { cout << "B constructed,yeah!/n"; }
virtual void disp() { cout << "This is B speaking!/n"; }
};
输出:
A constructed,yeah!
B constructed,yeah!
This is B speaking!
例乙:
class A {
public:
~A() { cout << "A destructed/n"; }
virtual void disp() { cout << "This is A speaking!/n"; }
};
class B : public A {
public:
~B() { cout << "B destructed/n"; }
virtual void disp() { cout << "This is B speaking!/n"; }
};
输出:
This is B speaking!
A destructed
例丙:
class A {
public:
A() { cout << "A constructed,yeah!/n"; }
~A() { cout << "A destructed/n"; }
virtual void disp() { cout << "This is A speaking!/n"; }
};
class B : public A {
public:
B() { cout << "B constructed,yeah!/n"; }
~B() { cout << "B destructed/n"; }
virtual void disp() { cout << "This is B speaking!/n"; }
};
输出:
A constructed,yeah!
B constructed,yeah!
This is B speaking!
A destructed
来个简单明晰点的:
class A {
public:
~A() { cout << "A destructed/n"; }
};
class B : public A {
public:
~B() { cout << "B destructed/n"; }
};
int main() {
A* pa = new B;
delete pa; // 输出 A destructed
return 0;
}
接下来我不禁要问了,为什么析构函数不像构造函数那样两个类的统统都调用呢?
答案明日揭晓!!