1. Assignment operator function

Question: The following is the declaration of the type CMyString, please add an assignment operator function to this type.

class CMyString
{
public:

    CMyString(char* pData = NULL);
    CMyString(const CMyString& str);
    ~CMyString(void);
    
private:
    char* m_pData;
};

The following four points should be paid attention to:
  1. Whether to declare the type of the return value as a reference of this type, and return the reference of the instance itself (ie *this) before the end of the function. Continuous assignment is allowed only if a reference is returned. Otherwise, if the return value of the function is void, applying the assignment operator will not be able to do continuous assignment. Assuming that there are 3 CMyString objects: str1, str2 and str3, the statement str1=str2=str3 in the program will not compile.
  
  2. Whether to declare the type of the incoming parameter as a constant reference. If the parameter passed in is not a reference but an instance, the copy constructor will be called once from the formal parameter to the actual parameter. Declaring parameters as references can avoid such fearless consumption and improve the efficiency of the code. At the same time, we will not change the state of the passed instance within the assignment operator function, so we should add the const keyword to the passed reference parameter.

  3. Whether to release its own memory. If we forget to free the space it already has before allocating new memory, the program will leak memory.

  4. Whether to judge whether the incoming parameter and the current instance (*this) are the same instance. If it is the same, no assignment operation is performed, and it is returned directly. If the assignment is made without judgment in advance, it will cause serious problems when releasing the memory of the instance itself: when *this and the incoming parameter are the same instance, then once the memory of the incoming parameter is released, the incoming parameter The memory is also freed at the same time, so there is no more content to be assigned to.

#include<iostream>
#include<cstring>
using namespace std;

class CMyString
{
public:

    CMyString(char* pData);
    CMyString(const CMyString& str);
    CMyString& operator =(const CMyString &str);
    ~CMyString(void);

private:
    char* m_pData;
};


CMyString& CMyString :: operator =(const CMyString &str) //返回类型声明为该类型的引用  传入的参数类型为常量引用
{
    if (this == &str)
    {
        return *this;
    }

    delete []m_pData;
    m_pData = NULL;
    m_pData = new char[strlen(str.m_pData) + 1];

    return *this;
}

int main()
{
    char a = 'ab';
    CMyString cm1(&a);

   return 0;
}

 

Exception Safety:

  In the previous function, we used delete to free the memory of the instance m_pData before allocating the memory. If the new char throws an exception due to insufficient memory at this time, m_pData will be a null pointer, which is very easy to cause the program to crash. That is to say, once an exception is thrown inside the assignment operator, the instance of CMyString no longer maintains a valid state. This violates the principle of Exception Safety.

  There are two ways to achieve exception safety in assignment operator functions: 1. First use new to allocate new content and then use delete to release existing content. In this way, the original content is released only after the allocation is successful, that is, when the allocation of memory fails, we can ensure that the instance of CMyString will not be modified. 2. Create a temporary instance first, and then swap the temporary instance with the original instance. Here is the code for the second idea:

CMyString& CMyString::operatoe =(const CMyString &str)
{
    if (this != &str)
    {
        CMyString strTemp(str);
        
        char* pTemp = strTemp.m_pData;
        strTemp.m_pData = m_pData;
        m_pData = pTemp;
    }
    
    return *this;
}

  In this function, we first create a temporary instance strTemp, and then exchange strTemp.m_pData with the m_pData of the instance itself. Since strTemp is a local variable, when the program runs outside the if, it will come out of the scope of the variable, and the destructor of strTemp will be automatically called to release the memory pointed to by strTemp.m_pData. Since the memory pointed to by strTemp.m_pData is the memory of m_pData before the instance, this is equivalent to automatically calling the destructor to release the memory of the instance.

  In the new code, we allocate memory with new in the CMyString constructor. If an exception such as bad_alloc is thrown due to insufficient memory, we have not modified the state of the original instance, so the state of the instance is still valid, which ensures exception safety.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324932059&siteId=291194637