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