EC笔记:第二部分:11:在operator=中处理“自我赋值”

已经一年半没有写过博客了,最近发现学过的知识还是需要整理一下,为知笔记,要开始收费了以前写在为知笔记上笔记也会慢慢的转到博客里。

 

话不多说,进入正题。

 

考虑考虑以下场景:

当某个对象对自身赋值时,会出现什么现象??

例子:

#include <iostream>

class A {

private:

    int *arr;

public:

    A() {

        arr = new int[256];

    }

    ~A() {

        delete arr;

    }

    const A& operator=(const A &other) {

        delete arr;                    //清除原来的值

        arr = new int[256];            //重新分配内存

        std::memcpy(arr, other.arr, 256 * sizeof(int));    //赋值

        return *this;

    }

};

 

 

在这段代码中,类A管理了256个整数的数组,当发生赋值操作时,对象先将自身管理的内存区释放,然后重新分配内存并且赋值(这里可以直接进行内存拷贝,为了演示,做了删除并重新分配操作,假设这里是个vector,想象一下^_^)。这个实现在应对大多数情况是没有问题的。如:

 

int main() {

    A a;

    A b;

    a = b;

}

这样完全没有问题。但是,假设出现以下场景:

int main() {

    A a;

    A &b = a;

 

    //若干操作

 

    a = b;

}

 

a和b表示的是同一个对象,那么在重新分配内存之前,就会将arr(a和b是同一个)指向的内存区域释放。然后在做memcpy的时候程序就会崩溃(引用了已释放的内存区域)。

 

重新对class A的operator=实现:

#include <iostream>

class A {

private:

    int *arr;

public:

    A() {

        arr = new int[256];

    }

    ~A() {

        delete arr;

    }

    const A& operator=(const A &other) {

        if(this == &other)

            return *this;

        delete arr;                    //清除原来的值

        arr = new int[256];            //重新分配内存

        std::memcpy(arr, other.arr, 256 * sizeof(int));    //赋值

        return *this;

    }

};

 

改进后,判断当前如果赋值和被赋值的是同一个对象,就直接返回,可以避免释放掉同一块内存。

 

这段代码虽然可以避免赋值上的问题,但是存在"异常安全性"的问题:试想,假设在new的时候抛出了一个异常(假设内存不足),那么,a在处理异常时,arr的状态就已经发生变化了。

 

另外,书中介绍了另一种避免赋值的时候释放掉有用内存的代码:

#include <iostream>

class A {

private:

    int *arr;

public:

    A() {

        arr = new int[256];

    }

    ~A() {

        delete arr;

    }

    const A& operator=(const A &other) {

        int *old_arr = arr;

        arr = new int[256];

        std::memcpy(arr, other.arr, 256 * sizeof(int));

        delete old_arr;

        return *this;

    }

};

 

这段代码中,先对原有的arr做一个备份,然后使用other对新分配的内存进行更新,最后释放掉原来arr指向的内存区域。

即使没有"证同测试",这段代码也能正常工作,因为释放动作在赋值动作之后,这是后就真的存在两个副本了(如果*this和other指向不同的值,就是3个副本)。但是,这段代码显然在抛开"异常安全性"后在效率上比上面那段代码的效率低(即使两个对象指向同一内存,也要从新分配内存,并重新赋值)。所以,如果关心效率的话,应该在最前面增加"证同测试"。如下:

#include <iostream>

class A {

private:

    int *arr;

public:

    A() {

        arr = new int[256];

    }

    ~A() {

        delete arr;

    }

    const A& operator=(const A &other) {

        if (this==&other)

            return *this;

        int *old_arr = arr;

        arr = new int[256];

        std::memcpy(arr, other.arr, 256 * sizeof(int));

        delete old_arr;

        return *this;

    }

};

 

至此,一份"异常安全的"且"效率优秀的"operator=操作符就完成了

 

请记住:

  1. 确保operator=有良好的行为。
  2. 当某个函数要操作同类对象或者多个继承自同一类对象时,不仅要考虑每个对象不同时的处理,还要考虑当某些对象是同一个对象的处理,确保正确性。
posted @ 2016-12-20 00:23  SkyFireITDIY  阅读(214)  评论(0编辑  收藏  举报