多态是指发出同样的消息被不同类型的对象接收时有可能导致完全不同的行为;

多态的实现:函数重载;运算符重载;虚函数

为什么需要重载运算符?

在C++没有复数运算,进行复数运算之前我们要事先写一个复数类,复数的运算该如何设计?为了实现复数的加减,我们需要重载+、-运算符。

运算符重载的实质:是对已有的运算符赋予多重含义;在C++中预定义的运算符其运算对象只能是基本数据类型,而不适用自定义类型(如类);重载运算符是将指定的运算符的表达式转化为对运算符函数的调用,运算对象转化为函数的参数。经过重载的运算符其操作数中至少有一个自定义类型,. .*  :: ?:是不能被重载的。

运算符重载有两种形式:重载为类成员函数;重载为友元函数。

声明形式:函数类型 operator 运算符 (形参){……}

重载为类成员函数时,参数个数=原操作数-1(后置++,--除外);

重载为友元函数时,参数个数=原操作数个数,且至少有一个自定义类型的形参。

运算符成员函数的设计:

双目运算符B:如果要重载 B 为类成员函数,使之能够实现表达式 oprd1 B oprd2,其中 oprd1 为A 类对象,则 B 应被重载为 A 类的成员函数,形参类型应该是 oprd2 所属的类型。经重载后,表达式 oprd1 B oprd2 相当于 oprd1.operator B(oprd2)

将+、-运算符重载为复数类的成员函数

#include<iostream>
using namespace std;
class complex //复数类声明
{
public: //外部接口
 complex(double r=0.0,double i=0.0){real=r;imag=i;} //构造函数
 complex operator + (complex c2); //运算符+重载成员函数
 complex operator - (complex c2); //运算符-重载成员函数
 void display(); //输出复数
private: //私有数据成员
 double real; //复数实部
 double imag; //复数虚部
}; 
complex complex::operator +(complex c2) //重载运算符函数实现
{
 return complex(real+c2.real, imag+c2.imag); //创建一个临时无名对象作为返回值
}
complex complex::operator -(complex c2) //重载运算符函数实现
{
 return complex(real-c2.real, imag-c2.imag);  //创建一个临时无名对象作为返回值
}
void complex::display()
{
 cout<<"("<<real<<","<<imag<<")"<<endl;
}
int main() //主函数
{
 complex c1(5,4),c2(2,10),c3; //声明复数类的对象
 cout<<"c1=";c1.display();
 cout<<"c2=";c2.display();
 c3=c1-c2; //使用重载运算符完成复数减法
 cout<<"c3=c1-c2=";
 c3.display();
 c3=c1+c2; //使用重载运算符完成复数加法
 cout<<"c3=c1+c2=";
 c3.display();
}//c1=(5,4)
//c2=(2,10)
//c3=c1-c2=(3,-6)
//c3=c1+c2=(7,14)

前置单目运算符U:如果要重载U为类成员函数,使之能够实现表达式U oprd,其中oprd为A类的对象,则U应该被重载成A类的成员函数,没有形参;经过重载后,U oprd相当于 oprd.operator U();

后置单目运算符++和--:如果要重载 ++或--为类成员函数,使之能够实现表达式  oprd++ 或 oprd-- ,其中 oprd 为A类对象,则 ++或--  应被重载为 A 类的成员函数,且具有一个 int 类型形参。经重载后,表达式 oprd++ 相当于  oprd.operator ++(0)。

例如:
运算符前置++和后置++重载为时钟类的成员函数。
前置单目运算符,重载函数没有形参,对于后置单目运算符,重载函数需要有一个整型形参。
操作数是时钟类的对象。实现时间增加1秒钟。

#include<iostream>
using namespace std;
class Clock //时钟类声明
{
public: //外部接口
 Clock(int NewH=0, int NewM=0, int NewS=0);
 void ShowTime();
 void operator ++(); //前置单目运算符重载
 void operator ++(int); //后置单目运算符重载
private: //私有数据成员
 int Hour,Minute,Second;
};
Clock::Clock(int NewH, int NewM, int NewS) //构造函数
{
 if(0 <= NewH && NewH < 24 && 0 <= NewM && NewM < 60 && 0 <= NewS && NewS < 60)
 {    Hour=NewH;
       Minute=NewM;
       Second=NewS;
 }
 else
      cout<<"Time error!"<<endl;
}
void Clock::ShowTime() //显示时间函数
{
 cout<<Hour<<":"<<Minute<<":"<<Second<<endl;
}
void Clock::operator ++() //前置单目运算符重载函数
{
 Second++;
 if(Second>=60)
 {
      Second=Second-60;
      Minute++;
      if(Minute>=60)
      {
           Minute=Minute-60;
           Hour++;
           Hour=Hour%24;
      }
 }
 cout<<"++Clock: ";
}
void Clock::operator ++(int) //后置单目运算符重载
{  //注意形参表中的整型参数
 Second++;
 if(Second>=60)
 {
     Second=Second-60;
     Minute++;
     if(Minute>=60)
     {
         Minute=Minute-60;
         Hour++;
         Hour=Hour%24;
     }
 }
 cout<<"Clock++: ";
}
int main()
{
 Clock myClock(23,59,59);
 cout<<"First time output:";
 myClock.ShowTime();
 myClock++;
 myClock.ShowTime();
 ++myClock;
 myClock.ShowTime();
}//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: //外部接口
 complex(double r=0.0,double i=0.0){real=r;imag=i;} //构造函数
 friend complex operator + (complex c1,complex c2); //运算符+重载友元函数
 friend complex operator - (complex c1,complex c2); //运算符-重载友元函数
 void display(); //显示复数的值
private: //私有数据成员
 double real;
 double imag;
};   //显示函数实现
void complex::display()
{ cout<<"("<<real<<","<<imag<<")"<<endl;}
complex operator +(complex c1,complex c2) //运算符重载友元函数实现
{ return complex(c2.real+c1.real,c2.imag+c1.imag);}
complex operator -(complex c1,complex c2) //运算符重载友元函数实现
{ return complex(c1.real-c2.real,c1.imag-c2.imag);}
int main() //主函数
{
 complex c1(5,4),c2(2,10),c3;
 cout<<"c1=";c1.display();
 cout<<"c2=";c2.display();
 c3=c1-c2; //使用重载运算符
 cout<<"c3=c1-c2=";
 c3.display();
 c3=c1+c2; //使用重载运算符
 cout<<"c3=c1+c2=";
 c3.display();
}

静态绑定和动态绑定

绑定
程序自身彼此关联的过程,将程序中的操作调用与执行该操作的代码关联起来。
静态绑定
绑定过程出现在编译阶段,用对象名或者类名来限定要调用的函数。
动态绑定
绑定过程工作在程序运行时执行,在程序运行时才确定将要调用的函数。
#include<iostream>//静态绑定
using namespace std;
class Point
{ public:
 Point(double i, double j) {x=i; y=j;}
 double Area()  const{  return 0.0;}
  private:
 double x, y;
};
class Rectangle:public Point
{ public:
 Rectangle(double i, double j, double k, double l);
 double Area() const  {return  w*h;}
  private:
 double w,h;
};
Rectangle::Rectangle(double i, double j, double k, double l) :Point(i,j)
{ w=k;  h=l; }
void fun(Point &s)//用基类对象作为形参
{ cout<<"Area="<<s.Area()<<endl;  }
int main()
{
 Rectangle rec(3.0, 5.2, 15.0, 25.0);
 fun(rec);//调用函数时编译器不知道调用的是哪个类的函数,就只能调用基类的函数了。
}
运行结果:
Area=0
#include<iostream>//动态绑定
using namespace std;
class Point
{ public:
  Point(double i, double j) {x=i; y=j;}
  virtual double Area()  const{  return 0.0;}//虚函数,编译过程中凡事碰到该函数暂时搁置,到运行的时候再确定。
  private:
 double x, y;
};
class Rectangle:public Point
{ public:
  Rectangle(double i, double j, double k, double l);
  virtual double Area() const  {return  w*h;}
  private:
 double w,h;
};
Rectangle::Rectangle(double i, double j, double k, double l) :Point(i,j)
{ w=k;  h=l; }
void fun(Point &s)
{ cout<<"Area="<<s.Area()<<endl;  }
int main()
{
 Rectangle rec(3.0, 5.2, 15.0, 25.0);
 fun(rec);//调用的是派生类的fun函数了。
}//运行结果:
Area=375

虚函数是动态绑定的基础。是非静态的成员函数。在类的声明中,在函数原型之前写virtual。
virtual 只用来说明类声明中的原型,不能用在函数实现时。
具有继承性,基类中声明了虚函数,派生类中无论是否说明,同原型函数都自动为虚函数。
派生类中同名的虚函数的本质:
不是重载声明而是覆盖了从基类中继承过来的虚函数。
调用方式:通过基类指针或引用,执行时会根据指针指向的对象的类,决定调用哪个函数。

#include <iostream>
using namespace std;
class B0 //基类B0声明
{
public: //外部接口
 virtual void display(){cout<<"B0::display()"<<endl;} //虚成员函数
};
class B1: public B0 //公有派生
{
public:
 void display(){cout<<"B1::display()"<<endl;} //虚成员函数
};
class D1: public B1 //公有派生
{
public:
 void display(){cout<<"D1::display()"<<endl;} //虚成员函数
};
void fun(B0 *ptr) //普通函数
{
 ptr->display();
}
int main() //主函数
{
 B0 b0,*p; //声明基类对象和指针
 B1 b1; //声明派生类对象
 D1 d1; //声明派生类对象
 p=&b0;
 fun(p); //调用基类B0函数成员
 p=&b1;
 fun(p); //调用派生类B1函数成员
 p=&d1;
 fun(p); //调用派生类D1函数成员
}
//运行结果:
B0::display()
B1::display()
D1::display()

虚析构函数

当需要通过基类的指针或者引用去指向派生类的对象,通过该对象去调用派生类的函数的时候就需要将该函数写成虚函数;同样,当需要通过基类指针删除派生类的对象时,或者允许其他人通过指针调用对象的析构函数,并且被析构的对象时有重要的析构函数的派生类的对象时,就需要让基类的析构函数成为虚函数。

抽象类:带纯虚函数的类。class 类名{virtual 类型 函数名(参数表)=0;//纯虚函数}

作用:抽象类是为抽象和设计的目的而声明,将有关的数据和行为组织在一个继承层级结构中,保证派生类具有要求的行为;对于暂时无法实现的函数,可以声明为纯虚函数,留给派生类来实现。

抽象类只能是基类,不能声明抽象类的对象,构造函数不能是虚函数,析构函数可以是虚函数。

#include <iostream>
using namespace std;
class B0 //基类B0声明
{
public: //外部接口
 virtual void display()=0;//纯虚函数

};
class B1: public B0 //公有派生
{
public:
 void display(){cout<<"B1::display()"<<endl;} //虚成员函数
};
class D1: public B1 //公有派生
{
public:
 void display(){cout<<"D1::display()"<<endl;} //虚成员函数
};
void fun(B0 *ptr) //普通函数
{
 ptr->display();
}
int main() //主函数
{
 B0 *p; //声明基类指针,但不能声明其对象。
 B1 b1; //声明派生类对象
 D1 d1; //声明派生类对象
  p=&b1;
 fun(p); //调用派生类B1函数成员
 p=&d1;
 fun(p); //调用派生类D1函数成员
}
//运行结果:
B1::display()
D1::display()

C++——多态性的更多相关文章

  1. javaScript的原型继承与多态性

    1.prototype 我们可以简单的把prototype看做是一个模版,新创建的自定义对象都是这个模版(prototype)的一个拷贝 (实际上不是拷贝而是链接,只不过这种链接是不可见,给人们的感觉 ...

  2. Java多态性——分派

    一.基本概念 Java是一门面向对象的程序设计语言,因为Java具备面向对象的三个基本特征:封装.继承和多态.这三个特征并不是各自独立的,从一定角度上看,封装和继承几乎都是为多态而准备的.多态性主要体 ...

  3. 【C++】多态性(函数重载与虚函数)

    多态性就是同一符号或名字在不同情况下具有不同解释的现象.多态性有两种表现形式: 编译时多态性:同一对象收到相同的消息却产生不同的函数调用,一般通过函数重载来实现,在编译时就实现了绑定,属于静态绑定. ...

  4. java多态性,父类引用指向子类对象

    父类引用指向子类对象指的是: 例如父类Animal,子类Cat,Dog.其中Animal可以是类也可以是接口,Cat和Dog是继承或实现Animal的子类. Animal animal = new C ...

  5. 『c++』 模板(template)--- 参数化多态性

    ---恢复内容开始--- 题外话: 模板机制的设计和细节是由Bjarne Stroustrup在其1988年10月发表的名为“Parameterized Types for C++”一文中披露的. 引 ...

  6. Java对象的多态性(转型)

    多态性在面向对象中主要有两种体现: <1>方法的重载与覆写 <2>对象的多态性 对象的多态性:向上转型:子类对象-->父类对象,向上转型会自动完成 向下转型:父类对象-- ...

  7. 【转】C++多态性

    ----转自http://blog.csdn.net/hackbuteer1/article/details/7475622 C++编程语言是一款应用广泛,支持多种程序设计的计算机编程语言.我们今天就 ...

  8. 浅谈C++多态性

    本文转载至http://blog.csdn.net/hackbuteer1/article/details/7475622 总结: (1)区分概念:    重载----同一个类中,相同的函数名字,不同 ...

  9. Java基础之在窗口中绘图——利用多态性使用鼠标自由绘图(Sketcher 7 with a crosshair cursor)

    控制台程序. 在Sketcher中创建形状时,并不知道应该以什么顺序创建不同类型的形状,这完全取决于使用Sketcher程序生成草图的人.因此需要绘制形状,对它们执行其他操作而不必知道图形是什么.当然 ...

  10. java多态性

    多态分两种: (1)   编译时多态(设计时多态):方法重载. (2)   运行时多态:JAVA运行时系统根据调用该方法的实例的类型来决定选择调用哪个方法则被称为运行时多态.(我们平时说得多的事运行时 ...

随机推荐

  1. Oracle导出警告&ldquo;EXP-00003: 未找到段 (0,0) 的存储定义&rdquo;解决

    环境:CentOS7.4   Oracle11.2.0.4(搭建rac集群) 问题描述:在使用exp命令执行导出的时候,部分表提示“EXP-00003: 未找到段 (0,0) 的存储定义”警告. 问题 ...

  2. sql server 基本操作

    1输入如下命令,即可通过SQL Server命令行启动.停止或暂停的服务. SQL Server命令行如下: 启动SQL ServerNET START MSSQLSERVER 暂停SQL Serve ...

  3. bzoj3162独钓寒江雪

    题意 \(n\)阶树,求本质不同的独立集个数 做法 重新编号后重心是不变的,如果有两个重心,可以加个虚点 用树哈希判子树有多少个相同的子树,设某种有\(k\)个,如果原本方案数为\(x\)个 则方案数 ...

  4. C语言再学习part3—算法

    君子远庖厨,万物皆备于我.—孟子 这篇文章主要总结程序的主要要素,以及程序的构成是什么样子的.最后说说我学到的一种奇特的表示算法的方式—伪代码. 让我们开始吧! 一个程序应该包括以下两个主要要素: 1 ...

  5. JavaScript 浅复制和深复制

    浅复制只会复制第一层的元素,嵌套的元素还是原来的引用. const obj = { a: 1, b: 2 } const copyObj = Object.assign({}, obj) const ...

  6. redis集群&elasticSearch的认识

    elasticSearch_day01 1. Redis集群 1.1 什么叫集群 多台服务器集中在一起,实现同一业务 1.2 为什么集群 一台服务器不够,需要多台服务器支持,解决高并发,集群往往伴随分 ...

  7. C# 如何获取日期时间各种方法

    我们可以通过使用DataTime这个类来获取当前的时间.通过调用类中的各种方法我们可以获取不同的时间:如:日期(2019-01-09).时间(16:02:12).日期+时间(2019-01-09 16 ...

  8. sqlserver2014部署安装

    百度云网址链接: https://pan.baidu.com/s/1BwgdnESI8Fqlos9EIOLv1A 提取码: wsy5 1.解压ISO镜像文件,点击setup安装程序 2.进入安装界面 ...

  9. C语言 if

    C语言 if C语言支持最基本的三种程序运行结构:顺序结构.选择结构.循环结构. 顺序结构:程序按顺序执行,不发生跳转. 选择结构:依据是否满足条件,有选择的执行相应功能. 循环结构:依据条件是否满足 ...

  10. Python函数进阶:闭包、装饰器、生成器、协程

    返回目录 本篇索引 (1)闭包 (2)装饰器 (3)生成器 (4)协程 (1)闭包 闭包(closure)是很多现代编程语言都有的特点,像C++.Java.JavaScript等都实现或部分实现了闭包 ...