1. 程式人生 > >C++ 過載運算子和過載函式

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。