C++ 多态
重载与多态
一、基本介绍
多态是指同样的消息被不同类型的对象接受时导致不同的行为。所谓消息是指对类的成员函数的调用,不同的行为是指不同的实现,也就是调用了不同的函数。
多态可以简单地理解为同一条函数调用语句能调用不同的函数;或者说,对不同对象发送同一消息,使得不同对象有各自不同的行为。
二、多态的实现
代码:
#include <iostream>
using namespace std;
class A
{
public:
virtual void Print() { cout << "A::Print" << endl; }
};
class B : public A
{
public:
virtual void Print() { cout << "B::Print" << endl; }
};
class D : public A
{
public:
virtual void Print() { cout << "D::Print" << endl; }
};
class E : public B
{
virtual void Print() { cout << "E::Print" << endl; }
};
int main()
{
A a; B b; D d; E e;
A *pa = &a; B *pb = &b;
pa->Print(); //多态, a.Print()被调用,输出:A::Print
pa = pb; //基类指针pa指向派生类对象b
pa->Print(); //b.Print()被调用,输出:B::Print
pa = &d; //基类指针pa指向派生类对象d
pa->Print(); //多态, d. Print ()被调用,输出:D::Print
pa = &e; //基类指针pa指向派生类对象d
pa->Print(); //多态, e.Print () 被调用,输出:E::Print
return 0;
}
运行结果:
在此程序中,四个类的派生关系如下图:
多态运行的规则:
每个类都有同名、同参数表的虚函数 Print(每个 Print 函数声明时都加了 virtual 关键字)。
根据多态的规则,对于语句“pa->Print()”,由于 Print 是虚函数,尽管 pa 是基类 A 的指针,编译时也不能确定调用的是哪个类的 Print 函数。当程序运行到该语句时,pa 指向的是哪个类的对象,调用的就是哪个类的 Print 函数。
三、运算符重载
1.运算符重载的意义
如下代码:
class Complex{
public:
Complex(double r=0.0,double i=0.0) : real(r) , imag(i){}
void display() const;
private:
double real;
double imag;
};
在C++中预定义的运算符的操作对象只能是基本数据类型。然而,对于如上代码中定义的复数类型,C++中预定义的运算符便无法实现运算操作。
所以,在此时,我们需要运算符重载,对已有的运算符赋予多重含义,使同一个运算符作用于不同类型的数据时导致不同的行为。
2.运算符重载的规则
重载的运算符是带有特殊名称的函数,函数名是由关键字 operator 和其后要重载的运算符符号构成的。与其他函数一样,重载运算符有一个返回类型和一个参数列表。
Box operator+(const Box&);
声明加法运算符用于把两个 Box 对象相加,返回最终的 Box 对象。大多数的重载运算符可被定义为普通的非成员函数或者被定义为类成员函数。如果我们定义上面的函数为类的非成员函数,那么我们需要为每次操作传递两个参数,如下所示:
Box operator+(const Box&, const Box&);
代码实例:
#include <iostream>
using namespace std;
class Box
{
public:
double getVolume(void)
{
return length * breadth * height;
}
void setLength( double len )
{
length = len;
}
void setBreadth( double bre )
{
breadth = bre;
}
void setHeight( double hei )
{
height = hei;
}
// 重载 + 运算符,用于把两个 Box 对象相加
Box operator+(const Box& b)
{
Box box;
box.length = this->length + b.length;
box.breadth = this->breadth + b.breadth;
box.height = this->height + b.height;
return box;
}
private:
double length; // 长度
double breadth; // 宽度
double height; // 高度
};
// 程序的主函数
int main( )
{
Box Box1; // 声明 Box1,类型为 Box
Box Box2; // 声明 Box2,类型为 Box
Box Box3; // 声明 Box3,类型为 Box
double volume = 0.0; // 把体积存储在该变量中
// Box1 详述
Box1.setLength(6.0);
Box1.setBreadth(7.0);
Box1.setHeight(5.0);
// Box2 详述
Box2.setLength(12.0);
Box2.setBreadth(13.0);
Box2.setHeight(10.0);
// Box1 的体积
volume = Box1.getVolume();
cout << "Volume of Box1 : " << volume <<endl;
// Box2 的体积
volume = Box2.getVolume();
cout << "Volume of Box2 : " << volume <<endl;
// 把两个对象相加,得到 Box3
Box3 = Box1 + Box2;
// Box3 的体积
volume = Box3.getVolume();
cout << "Volume of Box3 : " << volume <<endl;
return 0;
}
运行结果:
注意:
在C++中,有些运算符是不能重载的,如下:
- .:成员访问运算符
- .*, ->*:成员指针访问运算符
- :::域运算符
- sizeof:长度运算符
- ?::条件运算符
- #: 预处理符号
四、虚函数
例子:
#include <iostream>
using namespace std;
//基类People
class People{
public:
People(char *name, int age);
void display();
protected:
char *m_name;
int m_age;
};
People::People(char *name, int age): m_name(name), m_age(age){}
void People::display(){
cout<<m_name<<"今年"<<m_age<<"岁了,是个无业游民。"<<endl;
}
//派生类Teacher
class Teacher: public People{
public:
Teacher(char *name, int age, int salary);
void display();
private:
int m_salary;
};
Teacher::Teacher(char *name, int age, int salary): People(name, age), m_salary(salary){}
void Teacher::display(){
cout<<m_name<<"今年"<<m_age<<"岁了,是一名教师,每月有"<<m_salary<<"元的收入。"<<endl;
}
int main(){
People *p = new People("王志刚", 23);
p -> display();
p = new Teacher("赵宏佳", 45, 8200);
p -> display();
return 0;
}
我们直观上认为,如果指针指向了派生类对象,那么就应该使用派生类的成员变量和成员函数,这符合人们的思维习惯。但是本例的运行结果却告诉我们,当基类指针 p 指向派生类 Teacher 的对象时,虽然使用了 Teacher 的成员变量,但是却没有使用它的成员函数,导致输出结果不伦不类(赵宏佳本来是一名老师,输出结果却显示人家是个无业游民),不符合我们的预期。
为了让基类指针能够访问派生类的成员函数,C++ 增加了虚函数。使用虚函数非常简单,只需要在函数声明前面增加 virtual 关键字。
使用虚函数后,代码如下:
#include <iostream>
using namespace std;
//基类People
class People{
public:
People(char *name, int age);
virtual void display(); //声明为虚函数
protected:
char *m_name;
int m_age;
};
People::People(char *name, int age): m_name(name), m_age(age){}
void People::display(){
cout<<m_name<<"今年"<<m_age<<"岁了,是个无业游民。"<<endl;
}
//派生类Teacher
class Teacher: public People{
public:
Teacher(char *name, int age, int salary);
virtual void display(); //声明为虚函数
private:
int m_salary;
};
Teacher::Teacher(char *name, int age, int salary): People(name, age), m_salary(salary){}
void Teacher::display(){
cout<<m_name<<"今年"<<m_age<<"岁了,是一名教师,每月有"<<m_salary<<"元的收入。"<<endl;
}
int main(){
People *p = new People("王志刚", 23);
p -> display();
p = new Teacher("赵宏佳", 45, 8200);
p -> display();
return 0;
}
有了虚函数,基类指针指向基类对象时就使用基类的成员(包括成员函数和成员变量),指向派生类对象时就使用派生类的成员。换句话说,基类指针可以按照基类的方式来做事,也可以按照派生类的方式来做事,它有多种形态。