• 《剑指Offer》面试题1:赋值运算符函数


    《剑指Offer——名企面试官精讲典型编程题》

    作者:何海涛


    一、书上原题再现

    面试题1:赋值运算符函数

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

    class CMyString

    {

    public:

        CMyString(char* pData = nullptr);

        CMyString(const CMyString& str);

        ~CMyString(void);

     

        void Print();

         

    private:

        char* m_pData;

    };


    二、涉及的知识点

     

                             


    三、解题过程

    赋值:

           int a;  //声明

           a = 10; //赋值

          

    运算符:

           + - * / = % < > ?: 等等

          

    赋值运算符:

           =

          

    赋值运算符函数:

           What?嗯?

    其实,这里涉及到了运算符重载的知识点。我一个遨游在Java大海里的浪子,突然灌一口C/C++的海水,感觉有点生涩。

    什么是运算符重载?

    简单讲,比如说:

           + 加号的本意是   1+1 = 2  在这里是 累加 的功能

           在类似于Java、Python这类高级语言中,当 + 加号 出现在字符串之间比如 "Hello" + "World !",那么这里的加号就会被用来实现 连接两个字符串 的功能。

           加号不干 累加 的功能,而是实现了 连接两个字符串 的功能,这就叫做 加法运算符的重载。

           运算符重载中有一点要求需要特别注意:重载运算符的参数至少应有一个是类对象(或类对象的引用)

      也就是说,参数不能全部是C++的基本类型,以防止用户修改用于基本类型数据成员的运算符的性质,如下面这样是不对的:

                         int operator + (int a,int b)

                         {

                                return(a-b);

                         }

      原来运算符+的作用是对两个数相加,现在企图通过重载使它的作用改为两个数相减。

      如果允许这样重载的话,那么表达式4+3,它的结果是7还是1呢?显然,这是绝对要禁止的。

                 

    Java八大基本数据类型:

                  byte   short        int          long

                  float   double     boolean         char

          

           基本数据类型是没有String的,String的本质是一个用户类。所以说当加号出现在两个字符串之间的时候,实现连接两个字符串这个功能是加法运算符的重载。

           但是,但是,但是,Java语言没有运算符重载:

                  Java doesn’t support user-defined operator overloading.

                    Java不支持用户自定义操作符重载。

                  The preferred approach is to define a method on your class to perform the action: a.add(b) instead of a + b.

                    首选的方法是在类中定义一个方法来实现这个功能,比如说通过 a.add(b) 来实现 a+b 的功能。

                  The only aspect of Java which comes close to “custom” operator overloading is the handling of + for strings,

                    Java唯一接近“自定义”运算符重载的方面是 + 用于字符串的处理,

                  which either results in compile-time concatenation of constants or execution-time concatenation using StringBuilder/StringBuffer.

                    这里的加法会造成在编译期或执行期使用StringBuilder/StringBuffer。

                 

           那么回到正题,这道题用的语言是C++,C++是有运算符重载的,给一个类添加赋值运算符函数。这个类就是一个字符串类,名为:CMyString

    赋值运算符函数,就是重载赋值运算符了。下面是该函数的声明:

           CMyString& operator = (const CMyString& str);

           CMyString& 是函数的类型   operator = 整体看成函数名 const CMyString&是参数的类型 str 是参数的引用名

           我们要做的就是书写函数体。

    初级程序员版:

    1     CMyString& operator = (const CMyString& str){

    2            if(this == &str){

    3                   return *this;

    4            }else{

    5                   delete []m_pData;

    6                   m_pData = NULL;

    7                   m_pData = new char[strlen(str.m_pData)+];

    8                   strcpy(m_pData, str.m_pData);

    9                   return *this;

    10          }

    11   }

           这一版本的Bug之处就在于,假如第5、6行代码执行完成之后,第7行代码在分配内存的时候,分配失败了,那么我原先的数据也就没有了。

    这样原先的对象就会成为一个空指针,想象一下,一个空指针在程序中,一旦程序后文又用到这个对象,那么这个程序极有可能崩溃!!!

    考虑到异常安全性,下面的代码是

    高级程序员版:

    1     CMyString& operator = (const CMyString& str){

    2            if(this == &str){

    3                   return *this;

    4            }else{

    5                   CMyString strTemp(str);

    6                   char* pTemp = strTemp.m_pData;

    7                   strTemp.m_pData = m_pData;

    8                   m_pData = pTemp;

    9                   return *this;

    10          }

    11   }     

     


    四、调试步骤

    使用DevC++进行调试

     

     


    五、总结

    不理解的地方:

                     CMyString strTemp(str);  看不懂这句代码,这句代码怎么就创建一个临时对象了,

    这难道不是 类型名   函数名  参数?如果是这样的话,那么这个函数连声明都没有直接就用了?

           既然临时对象一但运行结束就会被析构,那么为什么要进行交换?直接把临时对象的值赋值给当前对象的值不就行了?

           一行代码

                  m_pData = strTemp.m_pData

           不比三行代码强吗?

      如果是说不能这样直接进行赋值,需要有个 char* pTemp的中间变量,那也应该是

      两行代码:

                  char* pTemp = strTemp.m_pData;

    //            strTemp.m_pData = m_pData;

                  m_pData = pTemp;

      中间那行代码的意义何在,难不成是写交换算法写顺手了???

                 

      百思不得其......(所以我现在还不是高级程序员......)             

                 

    总结:

      1、 C++中通过运算符函数(关键字operator)可以对运算符进行重载;

      2、如果要为一个类写赋值运算符函数,需要考虑内存分配失败的异常情况下要原对象不能为空指针的情况;

      3、 一个对象的值,可以在创建的时候通过构造函数进行赋值,

        也可以把另一个对象的值通过赋值运算符赋值给当前对象,

        也可以通过赋值运算符进行多个对象的连续赋值,

        这,就是赋值运算符函数的意义;

      4、C++很飘逸,很成功,很失败,临时对象很难懂;

      5、这道题的精髓我认为是就两点:

        一是如何把这个operator = 函数完整的写出来,

        二是在写的过程需要考虑内存泄漏的情况。


    六、声明与致谢

    本题源代码请移步《剑指Offer》作者何海涛的GitHub:https://github.com/zhedahht/CodingInterviewChinese2/blob/master/01_AssignmentOperator/AssignmentOperator.cpp

          

    参考文章:(每篇文章我都会去点赞,好的文章就应该公诸于世,大家共同学习)

      博客园:

        颜之年《C++重载运算符的规则详解》:https://www.cnblogs.com/summernight/p/8541079.html

        同勉共进《一文说尽C++赋值运算符重载函数(operator=)》:https://www.cnblogs.com/zpcdbky/p/5027481.html#top

        默默淡然《运算符重载详解》:https://www.cnblogs.com/liangxiaofeng/p/4311796.html

      RUNOOB:

        Java基本数据类型:http://www.runoob.com/java/java-basic-datatypes.html

      CSDN:

        michellechouu《【C++】赋值运算符函数》:https://blog.csdn.net/michellechouu/article/details/47298445

        liaotl10《才知道java竟然没有运算符重载》:https://blog.csdn.net/liaotl10/article/details/74999757

        Do丶YouMissing《java中是否对“+”,“=”,“+=”重载》:https://blog.csdn.net/caonima0001112/article/details/50492718

        viclee108《浅析Python运算符重载》:https://blog.csdn.net/goodlixueyong/article/details/52589979

      技术小黑屋:https://droidyue.com/

  • 相关阅读:
    论文摘记 2017.4.6-4.9
    P4 PI库安装
    Markdown中的表格
    Easy install ryu
    《重构网络-SDN架构与实现》阅读随笔
    解决Github Desktop Repo publish 失败问题
    解决 "OperationalError: (sqlite3.OperationalError) no such table: ..."问题
    初始化 Flask 虚拟环境 命令
    Flask: socket.error: [Errno 48] Address already in use 问题
    【转】TCP端口号记录
  • 原文地址:https://www.cnblogs.com/littlecurl/p/operator.html
Copyright © 2020-2023  润新知