OOP筆記 (六) 運算符重載

OOP筆記 (六) 運算符重載

1.運算符重載

對抽象數據類型也能夠直接使用C++提供的運算符
• 程序更簡潔
• 代碼更容易理解
例如:
• complex z1和complex z2是兩個複數對象,求兩個複數的和, 希望能直接寫:z1 + z2
• 字符串 MyString str1和MyString str2,兩字符串拼接
str1+str2
在這裏插入圖片描述

2.函數定義說明:

(1)函數類型指定重載運算符的返回值類型(即運算結果類型);
(2)operator定義運算符重載函數的關鍵字;
(3)運算符指定要重載的運算符的名稱,例如+ - [];
(4)形參表中給出重載運算符所需要操作對象的參數和類型。
例如:3+4中+號對應的操作數類型是int
“hello”+ “world” = “helloworld”對應的類型是MyString

例子:實現複數的加減
運算符重載爲普通函數:

class Complex
{
public:
    Complex(double r=0.0,double i=0.0)
    {real=r;image=i;}
    friend Complex operator+(const Complex & a, const Complex & b);
    //+:運算符重載爲普通函數,如需訪問私有成員,必須設置爲友元函數
private:
    double real;
    double image;
};
Complex operator+ (const Complex & a, const Complex & b)
{
    return Complex( a.real+b.real, a.image+b.image);
}// (1)形參類型和個數
int main()
{
    Complex z1(10,20),z2(5,7),z;
    z=z1+z2; // (2)調用寫法
    return 0;
}

+:運算符重載爲成員函數:

//(2)運算符+號重載爲成員函數
Complex Complex::operator +(const Complex &a)
{ 
   return Complex(real+a.real,image+a.image);
}

<< :輸出運算符重載:

//(3)<<輸出運算符重載 
ostream& operator<<(ostream& out, const Complex & a)
{
    out<<a.real<<":"<<a.image<<endl;
    return out;
}

【注意】(1)運算符左邊是輸出標準對象(第一個操作數),右邊是輸出內容(第二個操作數),只能重載爲友元函數
(2)返回值滿足輸出運算符能做拼接的要求,參數是引用,返回類型是引用

>> :輸入運算符重載

//(4)>> 輸入運算符重載
istream& operator>>(istream& in, Complex & a)
{
    in>>a.real>>a.image;
    return in;
}

放在一起:

class Complex
{
public:
    Complex(double r=0.0,double i=0.0)
    {real=r;image=i;}
    void display();
    //(1) '+'運算符重載爲友元函數
    friend Complex operator+ (const Complex & a, const Complex & b);
 
    //(2)運算符+號重載爲成員函數
    // Complex operator +(const Complex &a);
    //(3)<<輸出運算符重載
    friend ostream& operator<<(ostream& out, const Complex & a);
    friend istream& operator>>(istream& in,  Complex & a);
private:
    double real;
    double image;
};
//(1)'+'運算符重載爲友元函數
Complex operator+ (const Complex & a, const Complex & b)
{
    return Complex( a.real+b.real, a.image+b.image);
}
//(2)運算符+號重載爲成員函數
//Complex Complex::operator +(const Complex &a)
//{
 
//   return Complex(real+a.real,image+a.image);
//}
//(3)<<輸出運算符重載
ostream& operator<<(ostream& out, const Complex & a)
{
    out<<a.real<<":"<<a.image<<endl;
    return out;
}
//(4)>> 輸入運算符重載
istream& operator>>(istream& in,  Complex & a)
{
    in>>a.real>>a.image;
    return in;
}

int main()
{
    Complex z1(10,20),z2,z;
    cin>>z2;
    z=z1+z2;
    cout<<z;
    return 0;
}

自加 ++/自減–-:

#include <iostream>
#include<cstdio>
using namespace std;
class CDemo
{
public:
    CDemo(int x1):x(x1){}
    CDemo &operator ++() //(1)前置++:重載爲成員函數
    {  
        ++x;             //前置++運算結果滿足左值,返回類型必須是引用
        return *this;    //返回當前對象的值,this表示對象的地址
    }
    friend  CDemo operator ++(CDemo &a,int t);
    friend ostream & operator<<(ostream &out,const CDemo &d1);
private:
    int x;
};

CDemo operator ++(CDemo &a,int t) //(2)後置++:重載爲友元函數
{  
    CDemo old=a;
    a.x++;
    return old;  //後置++運算結果返回值,返回類型不是引用
}

ostream & operator<<(ostream &out,const CDemo &d1) //(3)<<:輸出運算符重載
{
    out<<d1.x;
    return out;
}
3.運算符重載爲友元函數

運算符也重載爲類的友元函數,這樣,它就可以自由訪問該類的任何數據成員。這時,運算所需要的操作數都需要通過函數的形參表來傳遞。
將運算符重載爲友元函數的情況:
• 成員函數不能滿足使用要求
• 普通函數, 又不能訪問類的私有成員
運算符左側的操作數與函數第一個參數對應,右側的和第二個參數對應。
當重載友元函數時,將沒有隱含的參數 this 指針。這樣,
對雙目運算符,友元函數有兩個參數,
對單目運算符,友員函數有一個參數。

4.運算符重載的注意事項

(1)C++不允許定義新的運算符,重載後運算符的含義應符合日常習慣
complex_a + complex_b
word_a > word_b
(2)重載後運算符的優先級和結合性都不會改
(3)一般重載的功能應當與原有功能相類似(運算符重載主要是針對新類型數據的實際需要,而對原有運算符進行適當的改造),不能改變原來運算符的操作對象個數,同時至少要有一個操作對象是自定義類型。
(4)重載運算符(), [], ->或者賦值運算符=時, 重載函數必須聲明爲類的成員函數
在這裏插入圖片描述

5.特殊運算符重載
1、=: 賦值運算符

賦值運算符 兩邊的類型 可以 不匹配
(1)把一個 int類型變量 賦值給一個 Complex對象
(2)把一個 char * 類型的字符串 賦值給一個 字符串對象
需要 重載賦值運算符 ‘=’
賦值運算符 ‘=’只能重載爲 成員函數

(1)淺拷貝存在問題
有兩個致命的錯誤
①當我們通過s2修改它的str時,s1的str同時被修改!
②當執行s2和s1的析構函數時,會導致同一內存區域釋放兩次,程序崩潰!
在這裏插入圖片描述
(2) 深複製/深拷貝(顯示定義賦值運算符重載): 將一個對象中指針變量指向的內容,複製到另一個對象中指針成員對象指向的地方
在這裏插入圖片描述

//(1)= :深複製,參數是const MyString&
MyString& MyString::operator = (const MyString& s)
{
    if(str)delete [] str;
    if(s.str)
    { 
        int i;for(i=0;s.str[i];i++);
        str=new char[i+1];
        for(i=0;s.str[i];i++) str[i]=s.str[i]; 
        str[i]='\0'; //strcpy() 
    }
    else  str=NULL; return *this;
}
2、 []:下標運算符重載

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
運算符代碼實現:

#include<iostream> 
using namespace std;
class MyString
{
private:
    char * str;
public:
    MyString():str(NULL) { } //構造函數, 初始化str爲NULL
    const char * c_str() { return str; }//返回str成員內容
    //(1)= :賦值運算符重載,參數類型是const 字符串
 
    MyString& operator = (const char * s);
    //(1)= :賦值運算符重載,參數是const MyString&
    MyString& operator = (const MyString& s);
    //(3)深拷貝:複製構造函數
    MyString(MyString &s);
//(4)強制轉換
operator char *()
    {
        return str;
}
int size()
{
int i;       
for(i=0;str[i];i++);
    return i;
}//(2)[]: 下標運算符重載
char &operator[](int i);
    ~MyString( );
};
//(1)= :賦值運算符重載
MyString& MyString::operator = (const char * s)
{
    if(str)delete [] str;
    if(s)
    {   int i;for(i=0;s[i];i++);
        str=new char[i+1];
        for(i=0;s[i];i++) str[i]=s[i];
        str[i]='\0';
    }
    else  str=NULL;    return *this; 
}
//(3)深拷貝:複製構造函數
MyString::MyString(MyString& s)
{
    if(s.str)
    {
        int i;for(i=0;s.str[i];i++);
        str=new char[i+1];
        for(i=0;s.str[i];i++) str[i]=s.str[i];
        str[i]='\0'; //strcpy()
    }
    else  str=NULL;
}
//(1)= :深複製,參數是const MyString&
MyString& MyString::operator = (const MyString& s)
{
    if(str==s.str) return *this;
    if(str)delete [] str;
    if(s.str)
    {
 
        int i;for(i=0;s.str[i];i++);
        str=new char[i+1];
        for(i=0;s.str[i];i++) str[i]=s.str[i];
        str[i]='\0';
    }
    else  str=NULL;    return *this;
}
 
//(2)[]: 下標運算符重載
char & MyString::operator[](int i)
{
    if(i<0||i>=size())
        throw "index is below or exceed";
    else        return  str[i];
}
MyString::~MyString()
{
    if(str)
        delete []str;
}
int main()
{
MyString s1,s2;
s1="this";
cout<<(char *)s1<<endl;
    s1=s1;
    s2="that";
    MyString s3=s2;
    cout<<(char *)s1<<endl;
    cout<<s3.c_str()<<endl;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章