最近在学习运算符重载和构造析构函数的时候,在重载+的时候,等号左边的值经常出现一个问题,经过一天多的研究,终于将这个问题解决了,现在将这个问题的解决方法记录下来,供大家互相学习。初次发文,不足之处还请多多包含!
问题:重载+之后,等号左边的值出现错误。
解决方案:重载=。以下是一些详细的说明。
/*-------------------------------------------- 写一个字符串类CString 1.重载+,实现“123”+“456”(=“123456”); 2.重载+=,实现“123”+=“456”(=“123456”); 3.重载=; 4.重写拷贝构造函数; --------------------------------------------*/ #include
写一个字符串类CString 1.重载+,实现“123”+“456”(=“123456”); 2.重载+=,实现“123”+=“456”(=“123456”); 3.重载=; 4.重写拷贝构造函数; --------------------------------------------*/ #include#include 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; } #include 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的指针分配的内存时会出错。其实,此块内存在此之前已经被释放掉了,重复释放引发错误。