運算符重載+構造析構函數

 

最近在學習運算符重載和構造析構函數的時候,在重載+的時候,等號左邊的值經常出現一個問題,經過一天多的研究,終於將這個問題解決了,現在將這個問題的解決方法記錄下來,供大家互相學習。初次發文,不足之處還請多多包含!

問題:重載+之後,等號左邊的值出現錯誤。

解決方案:重載=。以下是一些詳細的說明。

/*--------------------------------------------
寫一個字符串類CString
1.重載+,實現“123”+“456”(=“123456”);
2.重載+=,實現“123”+=“456”(=“123456”);
3.重載=;
4.重寫拷貝構造函數;
--------------------------------------------*/
#include <string.h>
#include <stdio.h>
class CString
{
    char *m_str;
    int m_Length;
public:
    /*函數作用:無參構造函數*/
    CString();
    /*函數作用:用字符指針進行有參構造
    參數 ch:一個指向字符串的指針*/
    CString(const char * ch);
    /*函數作用:拷貝構造
    參數 str:一個CString類型的對象,以此對象爲模板,創建一個一模一樣的對象*/
    CString(const CString& str);
    /*函數作用:通過重載運算符+,實現將兩個字符串相加的操作
    參數 str:相加的第二個CString對象
    返回值:兩個對象相加後的結果*/
    CString operator+(const CString& str) const;
    /*函數作用:通過重載運算符+=,實現將一個字符串添加到另一個字符串末尾的操作
    參數 str:被添加到當前字符串(調用此函數的那個字符串)後面的CString對象*/
    void operator+=(const CString& str);
    /*函數作用:通過重載運算符=,實現賦值操作
    參數 str:源對象*/
    void CString::operator=(const CString & str);
    /*函數作用:返回對象中的字符串
    返回值:指向對象中字符串的指針*/
    char *GetStr();
    /*函數作用:析構函數 清空保存對象的內存空間,並釋放*/
    ~CString();
};
​
int main()
{
    CString str1("123");
    CString str2("456");
    CString str3;
    CString str4(str1 + str2);      (1)
    CString str5 = str1 + str2;     (2)
    str3 = str1 + str2;             (3)
    printf("%s+%s=%s\n", str1.GetStr(), str2.GetStr(), str4.GetStr());
    printf("%s+%s=%s\n", str1.GetStr(), str2.GetStr(), str3.GetStr());
    printf("str1=%s,str2=%s,", str1.GetStr(), str2.GetStr());
    str1 += str2;
    printf("str1+=str2,str1=%s\n", str1.GetStr());
    return 0;
}
CString::CString()
{
}
​
CString::CString(const char * ch)
{
    m_Length = strlen(ch);
    m_str = new char[m_Length + 1];
    strcpy_s(m_str, m_Length + 1, ch);
}
​
CString::CString(const CString & str)  
{
    //*this = str;(這個行語句調用了重載的運算符=,可以替代下面的三行語句。這行語句和下面的三行語句二選一)
    m_Length = str.m_Length;
    m_str = new char[m_Length + 1]; 
    strcpy_s(m_str, m_Length + 1, str.m_str);
}
​
 CString CString::operator+(const CString & str) const
{
    CString str_new;
    str_new.m_Length = m_Length + str.m_Length;
    str_new.m_str = new char[str_new.m_Length + 1];
    strcpy_s(str_new.m_str, m_Length + 1 , m_str);
    strcat_s(str_new.m_str, str_new.m_Length  + 1, str.m_str);       //這裏需要注意的是第二個參數爲目標字符串緩衝區的大小
    return str_new;     //return是先調用拷貝構造函數創建一個跟str_new對象一模一樣的臨時對象Temp,並且返回,然後調用析構函數,將對象str_new析構掉
}
​
void CString::operator+=(const CString & str)
{
    char *ch = new char[m_Length + 1];
    strcpy_s(ch, m_Length + 1, m_str);
    int length = strlen(str.m_str);
    m_Length += length;
    delete m_str;
    m_str = NULL;
    m_str = new char[m_Length + 1];
    strcpy_s(m_str, strlen(ch) + 1, ch);
    strcat_s(m_str, m_Length + 1 , str.m_str);
    delete[] ch;
    ch = NULL;
}
​
​
void CString::operator=(const CString & str)
{
    m_Length = strlen(str.m_str);
    m_str = new char[m_Length + 1];
    strcpy_s(m_str, m_Length + 1, str.m_str);
​
}
​
char * CString::GetStr()
{
    return m_str;
}
​
CString::~CString()
{
    delete[] m_str;     
    m_str = NULL;
}
寫一個字符串類CString
1.重載+,實現“123”+“456”(=“123456”);
2.重載+=,實現“123”+=“456”(=“123456”);
3.重載=;
4.重寫拷貝構造函數;
--------------------------------------------*/
#include <string.h>
#include <stdio.h>
class CString
{
    char *m_str;
    int m_Length;
public:
    /*函數作用:無參構造函數*/
    CString();
    /*函數作用:用字符指針進行有參構造
    參數 ch:一個指向字符串的指針*/
    CString(const char * ch);
    /*函數作用:拷貝構造
    參數 str:一個CString類型的對象,以此對象爲模板,創建一個一模一樣的對象*/
    CString(const CString& str);
    /*函數作用:通過重載運算符+,實現將兩個字符串相加的操作
    參數 str:相加的第二個CString對象
    返回值:兩個對象相加後的結果*/
    CString operator+(const CString& str) const;
    /*函數作用:通過重載運算符+=,實現將一個字符串添加到另一個字符串末尾的操作
    參數 str:被添加到當前字符串(調用此函數的那個字符串)後面的CString對象*/
    void operator+=(const CString& str);
    /*函數作用:通過重載運算符=,實現賦值操作
    參數 str:源對象*/
    void CString::operator=(const CString & str);
    /*函數作用:返回對象中的字符串
    返回值:指向對象中字符串的指針*/
    char *GetStr();
    /*函數作用:析構函數 清空保存對象的內存空間,並釋放*/
    ~CString();
};
​
int main()
{
    CString str1("123");
    CString str2("456");
    CString str3;
    CString str4(str1 + str2);      (1)
    CString str5 = str1 + str2;     (2)
    str3 = str1 + str2;             (3)
    printf("%s+%s=%s\n", str1.GetStr(), str2.GetStr(), str4.GetStr());
    printf("%s+%s=%s\n", str1.GetStr(), str2.GetStr(), str3.GetStr());
    printf("str1=%s,str2=%s,", str1.GetStr(), str2.GetStr());
    str1 += str2;
    printf("str1+=str2,str1=%s\n", str1.GetStr());
    return 0;
}
CString::CString()
{
}
​
CString::CString(const char * ch)
{
    m_Length = strlen(ch);
    m_str = new char[m_Length + 1];
    strcpy_s(m_str, m_Length + 1, ch);
}
​
CString::CString(const CString & str)  
{
    //*this = str;(這個行語句調用了重載的運算符=,可以替代下面的三行語句。這行語句和下面的三行語句二選一)
    m_Length = str.m_Length;
    m_str = new char[m_Length + 1]; 
    strcpy_s(m_str, m_Length + 1, str.m_str);
}
​
 CString CString::operator+(const CString & str) const
{
    CString str_new;
    str_new.m_Length = m_Length + str.m_Length;
    str_new.m_str = new char[str_new.m_Length + 1];
    strcpy_s(str_new.m_str, m_Length + 1 , m_str);
    strcat_s(str_new.m_str, str_new.m_Length  + 1, str.m_str);       //這裏需要注意的是第二個參數爲目標字符串緩衝區的大小
    return str_new;     //return是先調用拷貝構造函數創建一個跟str_new對象一模一樣的臨時對象Temp,並且返回,然後調用析構函數,將對象str_new析構掉
}
​
void CString::operator+=(const CString & str)
{
    char *ch = new char[m_Length + 1];
    strcpy_s(ch, m_Length + 1, m_str);
    int length = strlen(str.m_str);
    m_Length += length;
    delete m_str;
    m_str = NULL;
    m_str = new char[m_Length + 1];
    strcpy_s(m_str, strlen(ch) + 1, ch);
    strcat_s(m_str, m_Length + 1 , str.m_str);
    delete[] ch;
    ch = NULL;
}
​
​
void CString::operator=(const CString & str)
{
    m_Length = strlen(str.m_str);
    m_str = new char[m_Length + 1];
    strcpy_s(m_str, m_Length + 1, str.m_str);
​
}
​
char * CString::GetStr()
{
    return m_str;
}
​
CString::~CString()
{
    delete[] m_str;     
    m_str = NULL;
}

語句(1)和語句(2)的執行過程是一樣的。以語句(1)爲例說明其執行順序。

CString str4(str1 + str2);      (1)      (1)

第一步:調用運算符+的重載函數,進行計算。計算得到一個局部對象str_new。

第二步:運算符+重載函數返回。在重載函數返回的時候,先調用拷貝構造函數創建一個跟對象str_new一模一樣的臨時對象Temp,並且將臨時對象Temp返回,將臨時對象Temp的名字改成str4。(在這裏對象str4還沒有聲明,索性將返回的對象直接改名字(就是這麼任性!!!),免去了一次定義對象和一次釋放對象的麻煩。爲了方便可以這麼理解,具體操作過程請研究彙編)

第三步:調用析構函數。調用析構函數,將局部對象str_new析構。(第二步和第三步是return語句乾的活)

執行完第三步之後,語句(1)已經執行結束了。

下面說明,語句(3)執行順序。


CString str3;CString str3;
str3 = str1 + str2;             (3)             (3)

第一步:調用運算符+的重載函數,進行計算。計算得到一個局部對象str_new。

第二步:運算符+重載函數返回。在重載函數返回的時候,先調用拷貝構造函數創建一個跟對象str_new一模一樣的臨時對象Temp,並且將對象Temp返回。

第三步:調用析構函數。調用析構函數,將局部對象str_new析構。(第二步和第三步是return語句乾的活)

第四步:調用運算符=重載函數。調用運算符=重載函數,將返回的臨時對象Temp的值進行深拷貝,拷貝給已聲明的對象str3。

第五步:調用析構函數。在完成臨時對象Temp的深拷貝之後,調用析構函數,將臨時對象Temp析構掉。

說明:因爲在此之前,已經有了對對象str3的聲明,因此在執行語句(3)的時候,會進行深拷貝,將臨時對象拷貝給已有對象str3。然後將臨時對象Temp調用析構函數析構掉。

總結

在調用完運算符+重載函數之後,如果=左邊的對象沒有聲明,就直接將運算符+的重載函數返回的臨時變量的名字改爲=左邊的對象的名字。如果=左邊的對象,已經被聲明,以運算符+重載函數返回的臨時變量爲參數,=左邊的對象調用運算符=重載函數。等運算符=的函數返回之後,調用析構函數,將臨時對象析構掉。

注意:在這裏必須重載運算符=。如果不重載運算符=,語句(3)的=這裏會進行淺拷貝。而且,由於已經聲明瞭str3,程序會調用析構函數將運算符+重載函數返回的臨時變量析構掉。這樣,會導致以下兩個問題:

第一、由於進行了淺拷貝,對象str3中的指針和臨時對象中的指針指向了同一塊內存空間。在程序調用析構函數將臨時變量析構之後,對象str3中的指針爲野指針,對象str3不完整了。

第二、由於進行了淺拷貝,在程序結束前,調用析構函數,動態刪除給對象str3的指針分配的內存時會出錯。其實,此塊內存在此之前已經被釋放掉了,重複釋放引發錯誤。

 

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