[剑指offer]1.赋值运算符函数

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Tian_Luo_Girl/article/details/81278644

题目:如下为类型CMyString的声明,请为该类型添加赋值运算符函数。

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

赋值运算符函数和拷贝构造函数类似,都是用来改变一个对象的私有变量,两者的区别是,拷贝构造函数是在对象创建时进行初始化,而赋值运算符是在一个已有的对象上修改。

调用了拷贝构造函数:
情形1:

    CMyString s1("Hello world");

情形2:

    CMyString s2 = "Hello world";

调用了赋值运算符:

    CMyString s3;
    s3 = s1;

方法1:

    CMyString& CMyString::operator=(const CMyString& s)
    {
        if(this != &s)
        {
            char* tmp = new char[strlen(s.m_pData) + 1];
            strcpy(tmp,s.m_pData);
            delete[] m_pData;
            m_pData = tmp;
        }
        return *this;
    }

关注点:
1.返回值为自身的引用,允许连续赋值。
2.参数类型声明为常量引用,从时间的角度上,少调用了一次拷贝构造函数和析构函数,从空间的角度上,节省了一个对象的空间。赋值运算符函数不会改变传入的实例的状态,因此使用const修饰。
3.判断传入的参数和当前的实例是不是同一个实例,如果是同一个,则没有赋值的必要。
4.要先开辟空间再释放原先的内存,如果由于内存不足抛出异常,我们还没有修改原来实例的状态,因此实例的状态还是有效的,这也就保证了异常安全性。

这个代码还可以进行优化,借助构造函数完成内存分配和strcpy以及异常处理。

方法2:

    CMyString& CMyString::operator=(const CMyString& s)
    {
        if(this != &s)
        {
            CMyString tmp(s);
            swap(tmp.m_pData,m_pData);
        }
        return *this;
    }

这段代码先创建了一个临时实例tmp,接着把tmp.m_pData和实例自身的m_pData作交换。tmp是一个临时变量,在if结束后tmp生命周期终止,自动调用析构函数,释放掉交换后的空间,被s实例初始化的内存保存在实例自身的m_pData中。

这段代码还有一种写法
方法3:

    CMyString& CMyString::operator=(CMyString s)
    {
        if(this != &s)
        {
            swap(s.m_pData,m_pData);
        }
        return *this;
    }

参数不用const,也不用引用,这样就形成了一个天然的临时变量,函数调用时,实参通过拷贝构造函数初始化形参s,将s的m_pData和实例自身的m_pData交换,当函数结束时,s自动调用析构函数。

猜你喜欢

转载自blog.csdn.net/Tian_Luo_Girl/article/details/81278644