之前对C++的面向对象的概念总是不太能理解,在学习了面向对象的多态时,总算对面向对象有了一些认识。
1、函数重写
1.1 要介绍对象的多态,首先先介绍下类中成员函数的重写。先看一下这段代码:
class Parent
{
public:
void print()
{
printf("I am parent\n");
}
};
class Child : public Parent
{
public:
void print()
{
printf("I am Children\n");
}
};
子类重写了父类中的成员函数,因此在子类中print()函数覆盖了父类中的printf()函数。对于他们的调用方式有所不同:
int main(int argc, char *argv[])
{
Child child;
child.print();
child.Parent::print();
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
输出结果是:
这里在调用父类的成员函数时使用了作用域操作符。
1.2 当遇到指向类对象的指针时的调用方式
void run(Parent *p)
{
p->print();
}
int main(int argc, char *argv[])
{
Parent parent;
Child child;
run(&parent);
run(&child);
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
输出结果:
按照面向对象的理论,run(Parent* p)函数调用的print()成员函数应该根据对象的不同而调用不同的成员函数,当p指向父类对象时调用的是父类的成员函数,当指向子类对象时应该调用子类的成员函数。可是程序数据的结果表明两次都是调用了父类的成员函数,本没有体现面向对象的特性。
原因解释:
1、C++和C一样,是静态编译语言。
2、在编译时,编译器自动根据指针的类型判断指向的是一个什么样的对象。所以编译器认为父类指针指向的是父 类对象。
3、由于程序没有运行,所以不可能知道父类指针指向的具体是父类对象还是子类对象。
4、从程序安全的角度,编译器假设父类指针只指向父类对象,因此编译的结果为调用父类的成员函数。
2、多态
2.1多态的本质
根据实际的对象类型来判断重写函数的调用。如果父类指针指向的是父类对象则调用父类中定义的重写函数,如果父类指针指向的是子类对象则调用子类定义中的重写函数。
换句话说就是根据实际的对象类型决定函数调用语句的具体调用目标。同样的调用语句有多种不同的表现形态。
2.2C++中多态实现方式
C++中通过virtual关键字对多态进行支持。父类中使用virtual声明的函数在子类中被重写后即可展现多态特性。
#include <cstdlib>
#include <iostream>
#include <stdio.h>
using namespace std;
class Parent
{
public:
virtual void print()
{
printf("I am parent\n");
}
};
class Child : public Parent
{
public:
void print()
{
printf("I am Children\n");
}
};
void run(Parent *p)
{
p->print();
}
int main(int argc, char *argv[])
{
Parent parent;
Child child;
run(&parent);
run(&child);
cout << "Press the enter key to continue ...";
cin.get();
return EXIT_SUCCESS;
}
输出结果:
成员函数加上virtual关键字后有了新的名字:虚函数。它表现了多态的特性。子类重写的成员函数依然是虚函数,vrtual关键字可以省略。