1、多态性
多态性在C++中表现为统一形式的函数调用,可能调用不同的函数实现
1.1 编译时的多态性
对于一些函数的调用,如果编译器在编译时就可以确定索要调用函数是哪一个具体实现,这种多态性称为编译时多态性,也成为静态多态性
C++可以通过重载(函数重载或运算符重载)来实现编译时的多态性。
1.2 运行时的多态性
当函数的调用在编译时无法得知所调用的函数是哪一个实现是,需要在运行时才能决定,这种多态性称为动态多态性。
示例1.1
// 多态性与虚函数.cpp : 定义控制台应用程序的入口点。
//
// Data类声明
class Data
{
public:
Data(int x = 0, int y = 0); // 缺省构造函数
void set_xy(int x, int y);
int get_x() const;
int get_y() const;
long norm(void);
~Data()
{
}
Data& operator += (Data& add) // 重载运算符+=
{
m_x += add.m_x;
m_y += add.m_y;
return *this; // 返回当前对象
}
protected:
int m_x;
int m_y;
};
// Data类成员函数定义
Data::Data(int x, int y):m_x(x), m_y(y){} // 构造函数初始对保护成员化
void Data::set_xy(int x, int y)
{
m_x = x;
m_y = y;
}
int Data::get_x(void) const
{
return m_x;
}
int Data::get_y(void) const
{
return m_y;
}
long Data::norm(void)
{
return (m_x*m_x+m_y*m_y);
}
// T_Data类声明
class T_Data:public Data
{
public:
T_Data(int x = 0, int y = 0, int z = 0);
void set_xyz(int x, int y, int z);
int get_z(void);
long norm(void);
T_Data& operator +=(T_Data& add)
{
m_x += add.m_x;
m_y += add.m_y;
m_z += add.m_z;
return *this;
}
protected:
int m_z;
};// T_Data类成员函数定义
T_Data::T_Data(int x, int y, int z):Data(x, y), m_z(z){}
void T_Data::set_xyz(int x, int y, int z)
{
m_x = x;
m_y = y;
m_z = z;
}
int T_Data::get_z(void)
{
return m_z;
}
long T_Data::norm(void)
{
return m_x*m_x+m_y*m_y+m_z*m_z;
}
#include "stdafx.h"
#include
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
Data d1(10, 20);
Data d2;
T_Data d3(10, 20, 30);
T_Data d4;
d2.set_xy(20,40);
d4.set_xyz(5, 10, 15);
cout << "d1 = (" << d1.get_x() << "," << d1.get_y() << ")\n";
cout << "d2 = (" << d2.get_x() << "," << d2.get_y() << ")\n";
d2 += d1; // 定调用Data类重载运算符:+=
cout << "d2 = (" << d2.get_x() << "," << d2.get_y() << ")\n";
cout << "d3 = (" << d3.get_x() << "," << d3.get_y() << ","
<< d3.get_z() << ")\n";
cout << "d4 = (" << d4.get_x() << "," << d4.get_y() << ","
<< d4.get_z() << ")\n";
d4 += d3; // 定调用Data类重载运算符:+=
cout << "d4 = (" << d4.get_x() << "," << d4.get_y() << ","
<< d4.get_z() << ")\n";
cout << "d1's norm is " << d1.norm() << endl;
cout << "d4's norm is " << d4.norm() << endl;
return 0;
}
示例1.2
int _tmain(int argc, _TCHAR* argv[])
{
Data *p;
T_Data td(10, 20, 30);
cout << "td = (" << td.get_x() << "," << td.get_y() << ","
<< td.get_z() << ")\n";
p = &td; // 用指向基类指针指向派生类对象
cout << "td's norm is (*p)" << p->norm() << endl; //实际上p是指向基类的Data的norm()
cout << "td's norm is (td)" << td.norm() << endl;
return 0;
}
2、虚函数
2.1 表现形式
从表现形式上看是指那些被virtual修饰的成员函数。当一个成员函数在基类中被定义为虚函数,那么只要同名函数出现在派生类中,如果在类型、参数等方面均保持相同,那么即使在派生类中的相同函数前没有关键字virtual,它也被默认看成是一个虚函数。
2.2 虚函数的作用
在示例1.2中,我们可以看到,通过指向派生累的基类指针来调用成员函数norm(),则实际调用的是基类的成员函数norm()。如果通过派生类指针来调用成员函数,则使用派生类对象的成员函数norm(),这是非多态行为。
其实只要在声明基类即Data类的成员函数norm改为虚函数,即
virtual long norm(void);
注意:
1、若虚函数在类外实现,实现时不需要加关键字virtual,否则会报语法错误
2、基类(Data类)声明norm()函数为虚函数后,其派生类的norm()也自动的变为虚函数,因为二者形式一致。
这时p->norm()调用的是我们希望的td对象(T_Data类)的norm()函数而不是基类(Data类)对象的norm()函数。对于p这个基类指针来说,它可以调用同一层次结构不同类对象的虚函数。对同一消息,不同对象(p指向的不同对象)有不同的响应方式,这就是多态性。
2.2.1 虚函数表
如果将一个基类的指针(本来是用来指向基类对象的)指向派生类对象,则进行指针类型转换,将派生类对象的指针转换为基类指针,因此基类指针指向的是派生类对象的基类部分。
其实把类的一个函数声明为虚函数时,编译器就在类结构里加上一个指针,该指针被称为虚指针,它指向的是一个虚函数表(Vtable),该表包含了类中所有虚函数的地址,也包含其基类的虚函数的地址。这样,对于声明了虚函数的类对象,系统在得到对象的指针后会查找虚函数表,找到该对象的虚函数的函数指针后调用该函数,如果此虚函数未被派生类实现那么系统会调用其基类的虚函数。