C++ 過載運算子和過載函式
前言:
運算子過載和函式過載體現了面向物件技術的多型性。多型性機制不僅增加了面向物件軟體系統的靈活性,進一步減少了冗餘資訊,而且顯著提高了軟體的可重用性和可擴充性。 從實現的角度來講,多型性可以劃分為兩類:編譯時的多型性和執行時的多型性。在C++語言中,編譯時的多型性主要是通過函式過載和運算子過載實現的。 本文主要講的是編譯時多型,因為編譯時多型主要是通過函式過載和運算子過載實現的。 執行時多型之後再講。
C++允許在同一作用域中的某個函式和運算子指定多個定義,分別稱為函式過載和運算子過載。
一、C++ 中的函式過載
概念
過載:是指執行存在多個同名函式,而這些函式的引數列表不同(引數個數、型別、順序),不能使用返回值作為區分。 關鍵詞:同一作用域內,具有相同函式名,不同引數列表
原理
重新命名機制(name mangling): 編譯器根據函式不同的引數表,對同名函式的名稱進行函式修飾,從而這些同名函式就成為了不同的函式(至少對於編譯器來說是這樣的)。該過程在編譯器間就已經確定了,也就是說在編譯期就綁定了(早繫結)。
區分
C語言是不支援函式過載的,這裡涉及另外一個知識點:C++在相容C時的extern "C"的用法。
程式碼例項
#include<iostream>
using namespace std;
void print(int i)
{
cout<<"print a integer :"<<i<< endl;
}
void print(string str)
{
cout<<"print a string :"<<str<<endl;
}
int main()
{
print(12);
print("hello world!");
return 0;
}
二、C++ 中的運算子過載
2.0 使用全域性函式進行運算子過載(資料成員為public屬性)
C++就為運算子過載提供了一種方法,即運算子過載函式。其函式名字規定為operator後緊跟過載運算子。比如:operator+(),operator*()等。現在我們給出一個加法運算子的過載函式用於完成複數的加法運算:
#include <iostream>
using namespace std;
class Complex //複數類
{
public:
double real;//實數
double imag;//虛數
Complex(double real=0,double imag=0)
{
this->real=real;
this->imag=imag;
}
};
Complex operator+(Complex com1,Complex com2)//運算子過載函式
{
return Complex(com1.real+com2.real,com1.imag+com2.imag);
}
int main()
{
Complex com1(10,10),com2(20,20),sum;
sum=com1+com2;//或sum=operator+(com1,com2)
cout<<"sum的實數部分為: "<<sum.real<<endl;
cout<<"sum的虛數部分為: "<<sum.imag<<"i"<<endl;
return 0;
}
輸出:
sum的實數部分為: 30
sum的虛數部分為: 30i
上述示例中的運算子過載函式是不屬於任何的類,是全域性的函式。因為在Complex類(複數類)中的資料成員是公有的性質,所以運算子過載函式可以訪問。但如果定義為私有的呢,那該怎麼辦。其實,在實際的運算子過載函式聲明當中,有兩種方法: 定義其為要操作類的成員函式或類的友元函式。
2.1 運算子過載函式作為類的友元函式的形式:
友元函式過載雙目運算子(有兩個運算元,通常在運算子的左右兩則),引數表中的個數為兩個。若是過載單目運算子(只有一個運算元),則引數表中只有一引數。
class 類名
{
friend 返回型別 operator運算子(形參表);
}
類外定義格式:
返回型別 operator運算子(引數表)
{
函式體
}
1) 友元函式過載雙目運算子(+)
#include <iostream>
using namespace std;
class Complex //複數類
{
//私有資料
private:
double real;//實數
double imag;//虛數
public:
Complex(double real=0,double imag=0)
{
this->real=real;
this->imag=imag;
}
//友元函式過載雙目運算子
friend Complex operator+(Complex com1,Complex com2);
void showSum();
};
Complex operator+(Complex com1,Complex com2)//運算子過載函式
{
return Complex(com1.real+com2.real,com1.imag+com2.imag);
}
void Complex::showSum()
{
if(imag>=0)
{
cout<<real<<"+"<<imag<<"i"<<endl;
}
else
{
cout<<real<<imag<<"i"<<endl;
}
}
int main()
{
Complex com1(10,10),com2(20,20),sum;
sum=operator+(com1,com2);//或 sum=com1+com2
sum.showSum();
return 0;
}
2) 友元函式過載單目運算子(++):
#include <iostream>
using namespace std;
class Point//座標類
{
private:
int x;
int y;
public:
Point(int x,int y)
{
this->x=x;
this->y=y;
}
friend void operator++(Point& point);//友元函式過載單目運算子++
void showPoint();
};
void operator++(Point& point)//友元運算子過載函式
{
++point.x;
++point.y;
}
void Point::showPoint()
{
std::cout<<"("<<x<<","<<y<<")"<<std::endl;
}
int main()
{
Point point(10,10);
++point;//或operator++(point)
point.showPoint();//輸出座標值
return 0;
}
輸出
(11,11)
2.2 運算子過載函式作為類的成員函式的形式:
對於成員函式過載運算子而言,雙目運算子的引數表中僅有一個引數,而單目則無引數。同樣的是過載,為什麼和友元函式在引數的個數上會有所區別的。原因在於友元函式,沒有this指標。
class 類名
{
返回型別 operator 運算子(形參表);
}
類外定義格式:
返回型別 類名:: operator 運算子(形參表)
{
函式體;
}
1) 成員函式過載雙目運算子(+):
#include <iostream>
using namespace std;
class Complex //複數類
{
//私有資料
private:
double real;//實數
double imag;//虛數
public:
Complex(double real=0,double imag=0)
{
this->real=real;
this->imag=imag;
}
//成員函式過載雙目運算子
Complex operator+(Complex com1);
void showSum();
};
Complex Complex::operator+(Complex com1)
{
return Complex(real+com1.real,imag+com1.imag);
}
void Complex::showSum()
{
if(imag>=0)
{
cout<<real<<"+"<<imag<<"i"<<endl;
}
else
{
cout<<real<<imag<<"i"<<endl;
}
}
int main()
{
Complex com1(10,10),com2(200,200),sum;
sum=com1+com2;//或 sum=com1.operator+(com2);
sum.showSum();
return 0;
}
2)成員函式過載單目運算子(++)
#include <iostream>
using namespace std;
class Point//座標類
{
private:
int x;
int y;
public:
Point(int x,int y)
{
this->x=x;
this->y=y;
}
void operator++();//成員函式過載單目運算子++
void showPoint();
};
void Point::operator++()//成員運算子過載函式
{
++x;
++y;
}
void Point::showPoint()
{
std::cout<<"["<<x<<","<<y<<"]"<<std::endl;
}
int main()
{
Point point(10,10);
++point;//或operator++(point)
point.showPoint();//輸出座標值
return 0;
}
輸出
[11,11]
三、過載輸出運算子<<
#include<iostream>
using namespace std;
class People{
public:
People(int id,int pwd):m_id(id),m_pwd(pwd){}
~People(){}
friend ostream& operator<< (ostream &os,People p);
private:
int m_id;
int m_pwd;
};
ostream& operator << (ostream &os, const People p){
os<<"your id is:"<<p.m_id<<" and password is:"<<p.m_pwd<<endl;
return os;
}
int main() {
People a(1,123);
People b(2,456);
cout << a<<b;
}
輸出:
your id is:1 and password is:123
your id is:2 and password is:456
注: 輸出運算子的第一個形參是非常量ostream物件的引用。之所以非常量是因為向流寫入內容會改變其狀態;而該形參是引用是因為我們無法直接複製一個ostream物件。 第二個形參一般是常量引用,該常量是我們要列印的類型別,之所以是常量是因為我們列印一般不會改變物件內容;是引用是我們希望避免複製形參。 返回值返回的是ostream類物件的引用,為了與其他輸出運算子保持一致,operator<<一般要返回它的ostream形參。同時為了進行連續的運算,如cout<<a<<b; 先列印類物件a,返回物件引用後繼續列印b。