C++:03.運算符重載

運算符重載的目的:讓對象的運算表現的和內置類型的運算一樣。

運算符重載函數,返回值一般不會是void,例如:連續的=就無法用void實現,一般用類類型的引用,可以保證連續運算。

class CComplex   複數類
{
public:
    CComplex(int real = 10, int image = 10):mreal(real), mimage(image)
    {
    	cout << "CComplex(int, int)" << endl;
    }
    ~CComplex(){ cout << "~CComplex()" << endl; }
    CComplex(const CComplex &src):mreal(src.mreal), mimage(src.mimage)
    {
    	cout << "CComplex(const CComplex &src)" << endl;
    }
    CComplex& operator=(const CComplex &src)  只要返回的對象還存在就返回引用
    {
    	cout << "operator=" << endl;
	mreal = src.mreal;
	mimage = src.mimage;
	return *this;
    }
    CComplex operator+(const CComplex &src)   由於返回的是局部變量,出函數析構,所以不能返回引用
    {
	cout << "operator+" << endl;
	CComplex tmp;
	tmp.mreal = this->mreal + src.mreal;
	tmp.mimage = this->mimage + src.mimage;
	return tmp;
    }
private:
    int mreal;
    int mimage;
};

int main()
{
    CComplex comp1;
    CComplex comp2(20, 20);
    CComplex comp3 = CComplex(30, 30);

    調用對象的函數:相當於comp2.operator=(comp3)
    因爲返回引用所以很好的解決了連續賦值的問題。
    comp1 = comp2 = comp3;

    cout << "---------------------" << endl;
    
    相當於CComplex  comp1.operator+(comp2)
    CComplex comp4 = comp1 + comp2;

    cout << "---------------------" << endl;
    
    return 0;
}

運算符重載爲全局函數時,參數的個數等於運算符的目數(即操作數的個數)。爲了使用類裏的私有成員所以寫成友元函數。

運算符重載爲成員函數時,參數的個數等於運算符的目數減一。

編譯器尋找對象的成員方法時,如果對象沒有相應的運算符重載,就會去全局尋找運算符重載函數。

/*
C++的字符串操作  string類型
*/
class CMyString
{
public:  當然這個類前面構造析構啥的,我沒寫,僅僅對運算符重載舉個栗子

    bool operator>(const CMyString &src)    成員函數 
    首先 > 是個二目運算符,但其左側對象成爲了調用這個函數的對象,所以參數之有一個 
    {
        return strcmp(mpStr, src.mpStr) > 0;
    }

private:
    char *mpStr;

    friend ostream& operator<<(ostream& out, const CMyString& srv);
    friend CMyString operator+(const CMyString&src, const CMyString& srv);
};

ostream& operator<<(ostream& out, const CMyString& srv)   全局函數  
<<這玩意也是個二目的,但右側是cout不是對象,所以你沒辦法寫再成員函數裏
{
    cout << srv.mpStr;
    return out;
}
 
也是全局的  +也是個二目,而且兩側都是對象,爲啥不寫裏面??  
原因:你有可能是CMyString str2 = str1 + "ccc"或CMyString str1 = "ddd" + str2;
首先說前面內個:這個可以調用類裏的成員函數,將ccc隱式的轉換爲類類型,再相加,沒毛病。
但對後面這個來說,由於左側是字符串,不是類類型,所以你只能寫成全局函數外。
但你沒必要一次寫倆+的重載吧,所以爲保證這倆都能成功,直接一個全局搞定。
CMyString operator+(const CMyString&src, const CMyString& srv) 
{
    int size = strlen(src.mpStr) + strlen(srv.mpStr) + 1;
    char *p = new char[size];
    strcpy(p, src.mpStr);
    strcat(p, srv.mpStr);
    CMyString tmp(p);
    delete[]p;
    return tmp;
}

int main()
{
    CMyString str1;
    
    一個函數確保了這三個都能實現:
    CMyString str2 = str1 + "ccc";
    CMyString str3 = "ddd" + str1;
    CMyString str4 = str2 + str1;

    if (str5 > str6)
    {
        cout << "str5 > str6" << endl;
    }

    return 0;
}

其他類型  轉  類類型,看有沒有合適的構造函數
類類型  轉  其他類型,提供類型重載函數    
這倆無法共存,因爲如果存在編譯器就不清楚要調用哪個。

Test t1;
t1 = 20;其他類型  轉  類類型   會調用Test(20)顯示生成臨時對象。


class CInt
{
public:
    CInt(int a = 0) :ma(a){}

    //方法一: 類類型 轉換 爲整形
    operator int& ()  如果沒有&,返回得到就是一個立即數,立即數是無法++的,所以應該返回引用
    {
        return ma;
    }

    //方法二:使用運算符重載,將整形與對象的成員進行操作
    bool operator<(const CInt &src)
    {
	return ma < src.ma;
    }
    void operator++()
    {
        ++ma;
    }
    int& operator[](int *ar)//返回引用,及支持讀又能支持寫
    {
	return ar[ma];
    }

private:
    int ma;
};

int main()
{
    int array[5] = { 12, 4, 5, 6, 7 };
    CInt i = 0;
    for ( ; i < 5; ++i)
    {
        cout << array[i] << " ";  方法一
	cout << i[array] << " ";  方法二:因爲操作符的左側需要是對象,所以可以這麼改
    }
    cout << endl;
    return 0;
}

 如果對象有小闊號()運算符重載函數,稱爲函數對象,看起來像函數但其實是對象。


 對於++運算符重載:

前置++與後置++是有區別的。

CComplex operator++(int)  // a++ 後置++
{
    return CComplex(this->mreal++, this->mimage++);
    //CComplex temp = *this;邏輯代碼,但創建了臨時變量,應採用上面的方法。
    //this->mreal++;
    //this->mimage++;
    //return temp;
}

CComplex& operator++()    //++a  前置++
{
	++this->mreal;
	++this->mimage;
	return *this;
}

 由上面可知,使用前置++,效率會更好。


下列運算符不允許重載:
. , .* , :: , ?:,siezof


重載new和delete :

爲什麼要重載new和delete?

1、出於效率考慮:也許要創建和銷燬一個特定的類的非常多的對象以至於這個運算變成了速度的瓶頸。

2、堆碎片:分配不同大小的內存可能會在堆上產生很多碎片,以至於很快用完內存。雖然內存可能還有,但是由於都是碎片,也就找不到足夠大的內存塊滿足需要。通過爲特定類創建自己的內存分配器,可以確保這種情況不會發生。

編譯器看到new時,編譯器分配內存並調用構造函數,但是當重載new時,可以改變的只是內存分配部分。

重載的new必須有一個size_t參數。這個參數由編譯器產生並傳遞給我們,它是要分配內存的對象的長度。
void* operator new(size_t sz)
{
    printf("operator new: %d Bytes\n",sz);
    void* m = malloc(sz);
    if (!m) 
        puts("out of memory");
    return m;
}
返回值是一個void*,而不是指向任何特定類型的指針。所做的只是分配內存。

參考博文:https://www.cnblogs.com/BaiYiShaoNian/p/4681367.html

 

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