一、概念
多態是面向對象程序設計的三大特征之一。封裝性是基礎,繼承性是關鍵,多態性是補充,而多態又存在于繼承的環境之中。多態性的含義就是多種狀態。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、虛表是所有類對象實例共用的。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。