zoukankan      html  css  js  c++  java
  • 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. 当某个函数要操作同类对象或者多个继承自同一类对象时,不仅要考虑每个对象不同时的处理,还要考虑当某些对象是同一个对象的处理,确保正确性。
  • 相关阅读:
    js中的原生Ajax和JQuery中的Ajax
    this的用法
    static的特性
    时政20180807
    java compiler没有1.8怎么办
    Description Resource Path Location Type Java compiler level does not match the version of the installed Java project facet Unknown Faceted Project Problem (Java Version Mismatch)
    分词器
    [数算]有一个工程甲、乙、丙单独做,分别要48天、72天、96天完成
    一点感想
    解析Excel文件 Apache POI框架使用
  • 原文地址:https://www.cnblogs.com/SkyFireITDIY/p/6201174.html
Copyright © 2011-2022 走看看