多态是指发出同样的消息被不同类型的对象接收时有可能导致完全不同的行为;
多态的实现:函数重载;运算符重载;虚函数
为什么需要重载运算符?
在C++没有复数运算,进行复数运算之前我们要事先写一个复数类,复数的运算该如何设计?为了实现复数的加减,我们需要重载+、-运算符。
运算符重载的实质:是对已有的运算符赋予多重含义;在C++中预定义的运算符其运算对象只能是基本数据类型,而不适用自定义类型(如类);重载运算符是将指定的运算符的表达式转化为对运算符函数的调用,运算对象转化为函数的参数。经过重载的运算符其操作数中至少有一个自定义类型,..*
运算符重载有两种形式:重载为类成员函数;重载为友元函数。
声明形式:函数类型 operator 运算符(形参){……}
重载为类成员函数时,参数个数=原操作数-1(后置++,--除外);
重载为友元函数时,参数个数=原操作数个数,且至少有一个自定义类型的形参。
运算符成员函数的设计:
双目运算符B:如果要重载 B 为类成员函数,使之能够实现表达式 oprd1 Boprd2,其中 oprd1 为A 类对象,则 B 应被重载为 A 类的成员函数,形参类型应该是 oprd2所属的类型。经重载后,表达式 oprd1 B oprd2 相当于oprd1.operator B(oprd2)
将+、-运算符重载为复数类的成员函数
#include<iostream>
using namespace std;
class complex
{
public:
private:
};
complex complex::operator +(complexc2)
{
}
complex complex::operator -(complexc2)
{
}
void complex::display()
{
}
int main()
{
}//c1=(5,4)
//c2=(2,10)
//c3=c1-c2=(3,-6)
//c3=c1+c2=(7,14)
前置单目运算符U:如果要重载U为类成员函数,使之能够实现表达式Uoprd,其中oprd为A类的对象,则U应该被重载成A类的成员函数,没有形参;经过重载后,U oprd相当于oprd.operator U();
后置单目运算符++和--:如果要重载++或--为类成员函数,使之能够实现表达式
例如:
运算符前置++和后置++重载为时钟类的成员函数。
前置单目运算符,重载函数没有形参,对于后置单目运算符,重载函数需要有一个整型形参。
操作数是时钟类的对象。实现时间增加1秒钟。
#include<iostream>
using namespace std;
class Clock
{
public:
private:
};
Clock::Clock(int NewH, int NewM, intNewS)
{
}
void Clock::ShowTime()
{
}
void Clock::operator ++()
{
}
void Clock::operator ++(int)
{
}
int main()
{
}//First time output: 23:59:59
//Show myClock++: 23:59:59
//Show ++myClock: 0:0:1
运算符友元函数的设计:如果要重载一个运算符,使之能够用于操作某类对象的私有成员,可以将此运算符重载为该类的友元函数;函数的形参代表依自左向右的次序排列的各个操作数;后置单目运算符++、--的重载函数形参列表中要增加一个int,但是不必写形参名。
双目运算符 B重载后,表达式oprd1 B oprd2 等同于operator B(oprd1,oprd2 )
前置单目运算符 B重载后,表达式 B oprd 等同于operator B(oprd )
后置单目运算符 ++和--重载后,表达式 oprd B 等同于operator B(oprd,0 )
#include<iostream>
using namespace std;
class complex
{
public:
private:
};
void complex::display()
{
complex operator +(complex c1,complexc2)
{
complex operator -(complex c1,complexc2)
{
int main()
{
}
静态绑定和动态绑定
绑定
程序自身彼此关联的过程,将程序中的操作调用与执行该操作的代码关联起来。
静态绑定
绑定过程出现在编译阶段,用对象名或者类名来限定要调用的函数。
动态绑定
绑定过程工作在程序运行时执行,在程序运行时才确定将要调用的函数。
#include<iostream>//静态绑定
using namespace std;
class Point
{ public:
};
class Rectangle:public Point
{ public:
};
Rectangle::Rectangle(double i, double j, double k, double l):Point(i,j)
{
void fun(Point &s)//用基类对象作为形参
{
int main()
{
}
运行结果:
Area=0
#include<iostream>//动态绑定
using namespace std;
class Point
{ public:
};
class Rectangle:public Point
{ public:
};
Rectangle::Rectangle(double i, double j, double k, double l):Point(i,j)
{
void fun(Point &s)
{
int main()
{
}//运行结果:
Area=375
虚函数是动态绑定的基础。是非静态的成员函数。在类的声明中,在函数原型之前写virtual。
virtual 只用来说明类声明中的原型,不能用在函数实现时。
具有继承性,基类中声明了虚函数,派生类中无论是否说明,同原型函数都自动为虚函数。
派生类中同名的虚函数的本质:不是重载声明而是覆盖了从基类中继承过来的虚函数。
调用方式:通过基类指针或引用,执行时会根据指针指向的对象的类,决定调用哪个函数。
#include <iostream>
using namespace std;
class B0
{
public:
};
class B1: public B0
{
public:
};
class D1: public B1
{
public:
};
void fun(B0 *ptr)
{
}
int main()
{
}//运行结果:
B0::display()
B1::display()
D1::display()
虚析构函数
当需要通过基类的指针或者引用去指向派生类的对象,通过该对象去调用派生类的函数的时候就需要将该函数写成虚函数;同样,当需要通过基类指针删除派生类的对象时,或者允许其他人通过指针调用对象的析构函数,并且被析构的对象时有重要的析构函数的派生类的对象时,就需要让基类的析构函数成为虚函数。
抽象类:带纯虚函数的类。class类名{virtual 类型 函数名(参数表)=0;//纯虚函数}
作用:抽象类是为抽象和设计的目的而声明,将有关的数据和行为组织在一个继承层级结构中,保证派生类具有要求的行为;对于暂时无法实现的函数,可以声明为纯虚函数,留给派生类来实现。
抽象类只能是基类,不能声明抽象类的对象,构造函数不能是虚函数,析构函数可以是虚函数。
#include <iostream>
using namespace std;
class B0
{
public:
class B1: public B0
{
public:
};
class D1: public B1
{
public:
};
void fun(B0 *ptr)
{
}
int main()
{
} //运行结果:
B1::display()
D1::display()