• 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. 当某个函数要操作同类对象或者多个继承自同一类对象时,不仅要考虑每个对象不同时的处理,还要考虑当某些对象是同一个对象的处理,确保正确性。
  • 相关阅读:
    很简单的在Ubuntu系统下安装字体和切换默认字体的方法
    Qt添加驱动——Qt数据库之添加MySQL驱动插件
    qt 字体的相关问题
    qt configure参数配置介绍
    Qt封装QTcpServer参考资料--QT4中构建多线程的服务器
    Qt封装QTcpServer参考资料--QTcpServer多线程实现
    Qt封装QTcpServer参考资料--QT自带QTcpServer架构分析
    Qt Creator设置多核编译(-j8参数)
    QString::​arg的用法
    《Qt数据类型》--QByteArray,QString,int,hex之间的转化
  • 原文地址:https://www.cnblogs.com/SkyFireITDIY/p/6201174.html
Copyright © 2020-2023  润新知