赋值运算符函数

赋值运算符是算是比较经常要重载的一个运算符,简单但也有许多细节需要注意

1.返回的类型声明为该类型的引用,目的为了支持连续赋值。例如:str1=str2=str3;

2.传入的参数类型声明为常量引用。声明为引用是为了提高效率,减少创建零时变量的开销,声明为常量则是防止传入的实例被修改。

所以参数应该声明为 const Class &;

3.释放自身已有的内存。这点尤其需要注意,如果类成员中有指针变量,那么就要考虑到动态内存分配。执行赋值操作时要先释放自身已有空间,然后重新申请空间赋值。否则将会出现内存泄漏。

4.判断传入参数和当前实例(*this)是不是同一实例。这也是存在动态内存分配情况下需要重点注意的一点。如果事先不进行判断,那么假如传入同一个实例将会释放自身内存,再也找不到需要赋值的内容了。

考虑以上几点得到如下代码:

class MyString
{
public:
    MyString(char* pData =NULL);
    MyString(const MyString& str);
    ~MyString(void);
    
    MyString& operator=(const MyString& str);

private:
    char* m_pData;
};

MyString&  MyString::operator=(const MyString& str)
{
    if( this == &str)
        return *this;

    delete[] m_pData;
    m_pData=NULL;

    m_pData = new char[strlen(str.m_pData)+1];
    strcpy(m_pData,str.m_pData);
    return *this;
}

 

还有一点需要考虑:new char 分配内存失败!!如果失败,因为我们在此之前已经释放原先的实例成员变量的内存,所以此时即使抛出异常处理我们也无法得到原先的实例,原先实例不再有效。

假如内存不足导致new char 分配内存失败了我们应该怎么办,上面代码没有做处理。

下面是一种简单的办法来实现赋值操作,同时提供了异常安全性:

MyString&  MyString::operator=(const MyString& str)
{
    if( this != &str)
    {
         MyString strtemp(str);

         char* ptemp = strtemp.m_pData;
         strtemp.m_pData = m_pData;
         m_pData = ptemp;
    }

    return *this;
}

这里创建了一个临时变量实例,内存分配和释放就交给临时变量实例strtemp的构造函数和析构函数来处理。一旦创建成功就进行交换,所以析构的时候释放的就是原来的实例。如果内存不够创建临时变量失败那么就会抛出异常,此时还未改变原先实例,原先实例仍然有效。

 

ps:题外点,这里的copy构造函数传入的也是常量引用,假如不考虑效率问题,可以把引用去掉吗?当然不行!!会形成死循环哦。

学习笔记,如果各位有什么赐教的地方请不吝相赠!

posted @ 2013-03-01 15:09  没离开过  阅读(191)  评论(0)    收藏  举报