1. 程式人生 > 其它 >一個案例理解C++面向物件之運算子過載

一個案例理解C++面向物件之運算子過載

運算子過載是C++面向物件基礎知識,本文通過一個自定義的string類完全理解運算子過載並且能夠溫習const與引用的相關知識:

  1. 首先構造一個字串類,既然是字串類,基本的兩個屬性應該是必需的:字串和字串長度
// String類的組成
class String
{
private:
    char* str;
    int len;
}
  1. 其次為了完成字串的初始化需要定義建構函式,為了保證記憶體不洩露需要定義解構函式,為了保證深拷貝定義複製拷貝函式
class String
{
public:
    String();
    String(char* s);
    ~String();
    String(const String & s);
}
  1. 為了完成字串的運算操作需要過載‘+’,‘*’,‘<<','>>'運算子
// 使用const成員函式實現右乘即“ str*3 <=> strstrstr ”的功能
    String operator*(int num) const;
// 使用成員函式實現賦值運算即“ String1 = String2 = "string" ”的功能
    String & operator=(const String & s);
// 使用成員函式實現中括號索引運算即“ String[1] ”的功能
    char & operator[](int num);
// 使用const成員函式實現常物件的中括號索引運算即“ String[1] ”的功能
    const char & operator[](int num) const;
// 使用友元函式過載實現左乘即“ 3*str <=> strstrstr ”的功能
    friend String operator*(int num,const String & s);
// 使用友元函式實現加法即“ str1+str2 <=> str1str2 ”的功能
    friend String operator+(const String & s1,const String & s2);
// 使用友元函式實現輸出即“ cout<<str ”的功能
    friend std::ostream & operator<<(std::ostream & os,const String & s);
// 使用友元函式實現輸入即“ cin>>str ”的功能
    friend std::istream & operator>>(std::istream & is,String & s);
  1. 為了保證自定義String類和字串指標的等價轉換需要定義轉換函式
// 使用轉換函式完成類型別到字串指標的轉換即“ char* ch = String ”的功能
// 僅含一個引數的建構函式可以作為引數型別到類型別的轉換
// 使用explicit關鍵字限制只能進行強制型別轉換
    explicit operator char*();
  1. 完整的String類的定義如下:

String.h

class String
{
private:
    char* str;
    int len;
public:
// 建構函式和解構函式
    String();
    String(char* s);
    ~String();

// 過載運算子
    String operator*(int num) const;
    friend String operator*(int num,const String & s);
    friend String operator+(const String & s1,const String & s2);
    friend std::ostream & operator<<(std::ostream & os,const String & s);
    friend std::istream & operator>>(std::istream & is,String & s);

// 強制型別轉換函式
    explicit operator char*();
};
  1. 完整的實現為:

String.cpp

#include <iostream>
#include "String.h"
using namespace std;

String::String()
{
// 預設建構函式
    str = new char[1];  // 保證解構函式delete的一致性
    str[0] = '\0';
    len = 0;
}

String::String(const char* s)
{
// 使用字串指標構造
    len = strlen(s);
    str = new char[len+1];
    strcpy(str, s);
}

String::~String()
{
// 解構函式釋放記憶體
    len = 0;
    delete [] str;
}

String::String(const String & s)
{
// 複製建構函式
    len = s.len;
    delete [] str;
    str = new char[len+1];
    strcpy(str,s.str);
}

String String::operator*(int num) const
{
// 字串乘法(成員函式)
    String result;
    result.len=len*3;       
    delete [] result.str;   // 釋放原有記憶體
    result.str=new char[result.len+1];
    result.str[0]='\0';
    for(int i=0;i<num;i++)
        result.str=strcat(result.str,str);
    return result;
}

String & String::operator=(const String & s)
{
// 賦值過載函式需要注意:
// 1. 釋放以前的記憶體
// 2. 防止賦值給自身
// 3. 返回引用值
    if(this == &s)
        return *this;
    len=s.len;
    delete [] str;
    str=new char[len+1];
    strcpy(str,s.str);
}

char & String::operator[](int num)
{
// 中括號過載,因為結果可為可修改的左值,所以返回值為非const引用
    return str[num];
}

const char & String::operator[](int num) const
{
// 中括號過載,適用於常物件的資料訪問
    return str[num];
}

String operator*(int num,const String & s)
{
// 字串乘法(友元函式)
    return s*num;
}

String operator+(const String & s1,const String & s2)
{
// 字串加法
    String sum;
    sum.len = s1.len + s2.len;
    delete [] sum.str;
    sum.str = new char[sum.len+1];
    strcpy(sum.str,s1.str);
    strcat(sum.str,s2.str);
}

ostream & operator<<(ostream & os,const String & s)
{
// 重定義<<
    os<<s.str;
    return os;
}

istream & operator>>(istream & is,String & s)
{
// 重定義>>
    is>>s.str;
    s.len=strlen(s.str);
}

String::operator char*()
{
// 轉換函式(String類->字串指標)
    char* s=new char[len+1];
    strcpy(s,str);
    return s;
}
  1. 上述程式碼中涉及返回物件的選擇,const、引用和普通物件,需要從實際含義去理解