《剑指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/

 

posted @ 2018-10-22 20:22  littlecurl  阅读(2484)  评论(3编辑  收藏  举报