问题:如果子类定义了与父类原型相同的函数会发生什么?
1.1.1 在子类中定义与父类中原型相同的函数
1.1.2 函数重写只发生在父类与子类之间
1.2.1 父类中被重写的函数依然会继承给子类
1.2.2 默认情况下子类中重写的函数将隐藏父类中的函数
1.2.3 通过作用域分辨符::可以访问到父类中被隐藏的函数
Source Example 1:
#include
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
class Parent{
public:
void print()
{
printf("I am Parent!\n");
}
};
class Child : public Parent
{
public:
void print()
{
printf("I am Child!\n");
}
};
int main(int argc, char** argv) {
Child child;
/* 输出I am Child! */
child.print();
/* 输出I am Parent! */
child.Parent::print();
return 0;
}
Source Example 1.3:
#include
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
class Parent{
public:
void print()
{
printf("I am Parent!\n");
}
};
class Child : public Parent
{
public:
void print()
{
printf("I am Child!\n");
}
};
void run()
{
Child child;
Parent* pp = &child;
Parent& rp = child;
/* 输出I am Child! */
child.print();
/* 输出I am Parent! */
pp->print();
/* 输出I am Parent! */
rp.print();
}
int main(int argc, char** argv) {
run();
return 0;
}
问题所在:
1. C++和C相同,是静态(类型的静态)编译型语言
2. 在编译时,编译器自动根据指针的类型判断指向的是一个什么样的对象
3. 由于定义了Parent* pp, 所以编译器认为父类指针指向的是父类对象(根据赋值兼容性原则(子类是特殊的父类),这个假设合理)
4. 由于程序没有允许,所以不可能知道父类指针指向的具体的对象具体是父类还是子类
5. 从程序安全的角度,编译器假设父类指针只指向父类对象,因此编译结果为调用父类的成员函数
void howToParent(Parent* p)
{
/* 只会输出I am Parent!,因为p是一个Parent*/
p->print();
}
在编译这个函数的时候,编译器不可能知道p指针究竟指向了什么,但是编译器没理由报错。
于是,编译器认为最安全的做法是编译到父类的print函数,因为父类和子类肯定都有相同的print函数。
#include
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
class Boss {
private:
static Boss* pInstance;
public:
static Boss* GetBossInstance()
{
if (pInstance == NULL)
{
pInstance = new Boss;
}
return pInstance;
}
int Kill()
{
return 10;
}
~Boss()
{
pInstance = NULL;
}
return 8;
}
};
class newMaster : public Master{
public:
int EnghtSword()
{
return Master :: EnghtSword() * 2;
}
};
void filedPK(Boss* boss, Master* master)
{
/* 无论传入的是Master还是newMaster,都只会调用Master里面的EnghtSword()函数 */
if (boss->Kill() > master->EnghtSword())
{
printf("master is killed!\n");
}
else
{
printf("boss is killed!\n");
}
}
int main(int argc, char** argv) {
Boss* boss = Boss :: GetBossInstance();
Master master;
newMaster newm;
printf("Boss PK Master\n");
filedPK(boss, &master);
printf("\n");
printf("Boss PK newMaster\n");
filedPK(boss, &newm);
return 0;
}
2.1.1 根据实际对象类型来判断重写函数的调用
2.1.2 如果是父类指针指向的是父类对象,则调用父类中定义的函数
2.1.3 如果是父类指针指向的是子类对象,则调用子类中定义的重写函数
根据实际的对象类型决定函数调用语句的具体调用目标
多态:同样的调用语句有多种不同的表现形态
2.3.1 C++中通过virtual关键字对多态进行支持
2.3.2 使用virtual声明的函数被重写后即可展现多态特性
被virtual声明的函数就是虚函数
Source Example 2:
#include
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
class Boss {
private:
static Boss* pInstance;
public:
static Boss* GetBossInstance()
{
if (pInstance == NULL)
{
pInstance = new Boss;
}
return pInstance;
}
int Kill()
{
printf("Boss Kill()!\n");
return 10;
}
~Boss()
{
pInstance = NULL;
}
};
Boss* Boss :: pInstance = NULL;
class Master {
public:
/* 虚函数,在父类里面声明 */
virtual int EnghtSword()
{
printf("Master EnghtSword()!\n");
return 8;
}
};
class newMaster : public Master{
public:
/* 最规范的写法要在子类里面加上virtual关键字 */
virtual int EnghtSword()
{
printf("newMaster EnghtSword()!\n");
return Master :: EnghtSword() * 2;
}
};
void filedPK(Boss* boss, Master* master)
{
if (boss->Kill() > master->EnghtSword())
{
printf("master is killed!\n");
}
else
{
printf("boss is killed!\n");
}
}
int main(int argc, char** argv) {
Boss* boss = Boss :: GetBossInstance();
Master master;
newMaster newm;
printf("Boss PK Master\n");
filedPK(boss, &master);
printf("\n");
printf("Boss PK newMaster\n");
filedPK(boss, &newm);
return 0;
}