一、概念
多态是面向对象程序设计的三大特征之一。封装性是基础,继承性是关键,多态性是补充,而多态又存在于继承的环境之中。多态性的含义就是多种状态。C++语言中支持两种多态性。一种是函数重载和运算符重载,又被称为静态多态,另一种是动态联编和虚函数,被称为动态多态。在这里主要讨论第一种和第二种的虚函数。
1.函数重载
函数重载简单的说就是给一个函数名多个含义。C++语言中允许在相同的作用域内以相同的名字定义几个不同实现的函数。定义这种重载函数时要求函数的参数或者至少有一个类型不同,或者个数不同,或者顺序不同。参数个数和类型,顺序都相同,仅返回值不同的重载函数是非法的。因为编译程序在选择相同名字的重载函数是仅考虑函数参数列表,即要根据函数参数列表中的参数个数、参数类型或参数顺序的差异进行选择。
举例:string类的构造函数重载(编译环境:VS2013)
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;
#include <string.h>
class String
{
public:
String(char *s)
{
length = strlen(s);
ps = new char[length + 1];
strcpy(ps, s);
}
String(string &s)
{
length = s.length;
ps = new char[length + 1];
strcpy(ps, s.ps);
}
String(int size = 20)
{
length = size;
ps = new char[length + 1];
*ps = '\0';
}
~String()
{
delete ps;
}
int getlen()
{
return length;
}
void print()
{
cout << ps << endl;
}
private:
char* ps;
int length;
};
int main()
{
String str1("String");
str1.print();
cout << str1.getlen() << endl;
char *ps1 = "String Pionter";
String str2(ps1);
String str3(str2);
str3.print();
cout << str3.getlen() << endl;
system("pause");
return 0;
}
String类的构造函数被重载了三次,
str1("String")调用构造函数String(char *s);
str2(ps1)调用构造函数String(char *s);
str3(str2)调用构造函数String(String);
2.运算符重载
运算符重载重载就是赋予已有的运算符多重定义,使它具多种多种功能。
定义方法:类名 operator运算符(参数列表){ }
下面的运算符在C++中允许重载:
算数运算符: +, -, *, /, %, ++, --
位操作运算符: &,|, ~, ^, <<,>>
逻辑运算符: !, &&, ||
比较运算符: <, >, <=, >=, ==, !=
赋值运算符: =, +=, -=, *=, /=, %=, &=, |=, ^=, <<=, >>=
其它运算符: [], (), ->, ', new, delete, new[], delete[]
下列运算符不允许重载:
·,·*, ::,?
使用运算符重载的注意:
用户重新定义运算符时,不改变原运算符的优先级和结合性。不改变运算符的操作数个数和语法结构,单目运算符只能重载为单目运算符,双目运算符只能重载双目运算符。
重载运算符不能有二义性。
运算符重载举例:
//运算符重载
#include <iostream>
using namespace std;
class Complex
{
//friend Complex operator+(Complex c1, Complex c2);
public:
Complex(double real = 0.0, double p_w_picpath = 0.0)
: _real(real)
, _p_w_picpath(p_w_picpath)
{}
Complex(const Complex& c)
: _real(c._real)
, _p_w_picpath(c._p_w_picpath)
{}
Complex operator+(const Complex& c1)
{
return Complex(_real + c1._real, _p_w_picpath + c1._p_w_picpath);
}
Complex operator-(const Complex& c1)
{
return Complex(_real - c1._real, _p_w_picpath - c1._p_w_picpath);
}
Complex operator*(const Complex& c1)
{
return Complex(_real*c1._real - _p_w_picpath*c1._p_w_picpath, _real*c1._p_w_picpath + _p_w_picpath*c1._real);
}
Complex operator/(const Complex& c1)
{
return Complex((_real*c1._real + _p_w_picpath*c1._p_w_picpath) / (c1._p_w_picpath*c1._p_w_picpath + c1._real*c1._real), (_p_w_picpath*c1._real - _real*c1._p_w_picpath) / (c1._p_w_picpath*c1._p_w_picpath + c1._real*c1._real));
}
Complex& operator+=(const Complex& c1)
{
_real = _real + c1._real;
_p_w_picpath = _p_w_picpath + c1._p_w_picpath;
return *this;
}
Complex& operator-=(const Complex& c1)
{
_real = _real - c1._real;
_p_w_picpath = _p_w_picpath - c1._p_w_picpath;
return *this;
}
Complex& operator*=(const Complex& c1)
{
double tmp = _real;
_real = _real*c1._real - _p_w_picpath*c1._p_w_picpath;
_p_w_picpath = tmp*c1._p_w_picpath + _p_w_picpath*c1._real;
return *this;
}
Complex& operator/=(const Complex& c1)
{
double tmp = _real;
_real = (_real*c1._real + _p_w_picpath*c1._p_w_picpath) / (c1._p_w_picpath*c1._p_w_picpath + c1._real*c1._real);
_p_w_picpath = (_p_w_picpath*c1._real - tmp*c1._p_w_picpath) / (c1._p_w_picpath*c1._p_w_picpath + c1._real*c1._real);
return *this;
}
// 只比较实部
bool operator<(const Complex& c)
{
if (_real<c._real)
{
return true;
}
else
{
return false;
}
}
bool operator>(const Complex& c)
{
if (_real>c._real)
{
return true;
}
else
{
return false;
}
}
bool operator<=(const Complex& c)
{
if ((_real<c._real) || (_real == c._real))
{
return true;
}
else
{
return false;
}
}
Complex& operator=(const Complex& c)
{
if (this != &c)
{
_real = c._real;
_p_w_picpath = c._p_w_picpath;
}
return *this;
}
bool operator==(const Complex& c)
{
return _real == c._real && _p_w_picpath == c._p_w_picpath;
}
bool operator&&(const Complex& c)
{
return true;
}
// 前置
Complex& operator++()
{
_real++;
_p_w_picpath++;
return *this;
}
// 后置++
Complex operator++(int)
{
Complex temp(*this);
_real++;
_p_w_picpath++;
return temp;
}
Complex& operator--()
{
_real--;
_p_w_picpath--;
return *this;
}
Complex operator--(int)
{
Complex temp(*this);
_real--;
_p_w_picpath--;
return temp;
}
void display()
{
cout << _real << "," << _p_w_picpath << "i" << endl;
cout << endl;
}
private:
double _real;
double _p_w_picpath;
};
int main()
{
Complex c1(1, 1);
Complex c2(2, 1);
Complex c3;
Complex c4(1, 1);
Complex c5(1, 1);
bool bo = true;
c3 = c1 + c2;
cout << "c3 = c1 + c2 " << endl;
c3.display();
c3 = c1 - c2;
cout << "c3 = c1 - c2 " << endl;
c3.display();
c3 = c2*c1;
cout << "c3 = c1 * c2 " << endl;
c3.display();
c3 = c2 / c1;
cout << "c3 = c1 / c2 " << endl;
c3.display();
c1 += c2;
cout << " c1 += c2 " << endl;
c1.display();
c1 -= c2;
cout << " c1 -= c2 " << endl;
c1.display();
c1 *= c2;
cout << " c1 *= c2 " << endl;
c1.display();
c1 /= c2;
cout << " c1 /= c2 " << endl;
c1.display();
bo = c1 < c2;
if (bo)
{
cout << "c1 < c2 is true" << endl;
}
else
{
cout << "c1 < c2 is false" << endl;
}
c4++;
cout << " c4++ " << endl;
c4.display();
++c5;
cout << " ++c5 " << endl;
c5.display();
c4--;
cout << " c4-- " << endl;
c4.display();
--c5;
cout << " -c5 " << endl;
c5.display();
system("pause");
return 0;
}
对复数的四则运算,++,--(前置,后置)等进行了重载。
3.虚函数
使用virtual关键字修饰函数时,指明该函数为虚函数,派生类需要重新实现,编译器将实现动态绑定。
总结:
1、派生类重写基类的虚函数实现多态,要求函数名、参数列表、返回值完全相同。(协变除外)
2、基类中定义了虚函数,在派生类中该函数始终保持虚函数的特性。
3、只有类的成员函数才能定义为虚函数,静态成员函数不能定义为虚函数。
4、如果在类外定义虚函数,只能在声明函数时加virtual关键字,定义时不用加。
5、构造函数不能定义为虚函数,虽然可以将operator=定义为虚函数,但最好不要这么做,使用时容易混淆
6、不要在构造函数和析构函数中调用虚函数,在构造函数和析构函数中,对象是不完整的,可能会出现未定义的行为。
7、最好将基类的析构函数声明为虚函数。(析构函数比较特殊,因为派生类的析构函数跟基类的析构函数名称不一样,但是构成覆盖,这里编译器做了特殊处理)
8、虚表是所有类对象实例共用的。
亿速云「云服务器」,即开即用、新一代英特尔至强铂金CPU、三副本存储NVMe SSD云盘,价格低至29元/月。点击查看>>
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。