运算符重载+构造析构函数

 

最近在学习运算符重载和构造析构函数的时候,在重载+的时候,等号左边的值经常出现一个问题,经过一天多的研究,终于将这个问题解决了,现在将这个问题的解决方法记录下来,供大家互相学习。初次发文,不足之处还请多多包含!

问题:重载+之后,等号左边的值出现错误。

解决方案:重载=。以下是一些详细的说明。

/*--------------------------------------------
写一个字符串类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;
}
写一个字符串类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;
}

语句(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的指针分配的内存时会出错。其实,此块内存在此之前已经被释放掉了,重复释放引发错误。

 

你可能感兴趣的:(C++,运算符重载,构造函数,析构函数)