自我总结一下《剑指Offer》中的题目,也欢迎有兴趣的朋友提出意见或对错误进行指正。
第一题 赋值运算符函数
所需知识基础:拷贝构造,构造析构,重载操作符
题目:
这道题是面试时经常让手写的代码之一。主要考察的知识点正如书中所说:
1.是否把返回值类型声明为该类型的引用,并在函数结束前返回实例自身的引用(*this)。只有返回一个引用,才可以允许连续赋值。否则如果函数的返回值是void,则应用该赋值运算符将不能进行连续赋值。假设有3个CMyString的对象:str1、str2、str3,在程序中语句str1=str2=str3将不能通过编译。
2.是否把传入的参数的类型声明为常量引用。如果传入的参数不是引用而是实例,那么从形参到实参会调用一次拷贝构造函数。把参数声明为引用可以避免这样的无谓消耗,能提高代码的效率。同时,我们在赋值运算符函数内不会改变传入的实例状态,因此应该为传入的引用参数加上const关键字。
3.是否释放实例自身已有的内存。如果我们忘记在分配新内存之前释放已有的空间,则程序将出现内存泄漏。
4.判断传入的参数和当前的实例(*this)是不是同一个实例。如果是同一个,则不进行赋值操作,直接返回。如果事先不判断就进行赋值,那么在释放实例自身内存的时候就会导致严重的问题:当*this和传入的参数是同一个实例时,一旦释放了自身内存,传入的参数的内存也同时被释放了,因此再也找不到需要赋值的内容了。
关于上述需要第2点的解释:
如果允许拷贝构造函数传值,就会在拷贝构造函数内调用拷贝构造函数,会形成永无休止都得调用递归而栈溢出。例子如下:
class CBase
{
public:
int a;
//CBase(CBase base) //错 相当于CBase(CBase base = base)
//{ //相当于给形参又初始化,无限循环下去
// cout<<"CBase::CBase"<<endl;
//}
CBase(const CBase &base) //对 拷贝构造的形参应该是一个引用
{
cout<<"CBase::CBase"<<endl;
}
};
如果注意到上述几点,就可以写出本题代码:
CMyString &operator = (const CMyString &str)
{
//判断是否是同一个对象,是同一个就跳出函数
//怎么判断是不是同一个对象?
//对象是一个变量,变量是内存的别名,地址相同,对象就是一个
if(this == &str) return *this; /*返回一个对象*/
//回收之前的空间
delete []m_pData; /*构造函数中类成员是字符串数组,则应释放一个数组*/
m_pData = 0;
//申请新空间
m_pData = new char[strlen(str.m_pData)+1]; /*注意“\0”*/
//将内容拷贝过来
strcpy_s(m_pData,strlen(str.m_pData)+1,str.m_pData);
//返回一个对象
return *this;
}
另外,本题可以扩展为模仿写出String类的原理。还应涉及到字符串的拼接等功能。由于功能过多,我只写了一部分,供大家参考:
#include <iostream>
using namespace std;
class CMyString
{
private:
char *m_pStr;
public:
CMyString()
{
m_pStr = new char[2];
m_pStr[0] = 0;
m_pStr[1] = 0;
}
CMyString(const char* pStr)
{
if (!pStr)
{
m_pStr = new char[2];
m_pStr[0] = 0;
m_pStr[1] = 0;
return;
}
m_pStr = new char[strlen(pStr)+1];
strcpy_s(m_pStr,strlen(pStr)+1,pStr);
}
CMyString(const char Str)
{
m_pStr = new char[2];
m_pStr[0] = Str;
m_pStr[1] = 0;
}
CMyString(CMyString &Str)
{
m_pStr = new char[strlen(Str.m_pStr)+1];
strcpy_s(m_pStr,strlen(Str.m_pStr)+1,Str.m_pStr);
}
~CMyString()
{
if (m_pStr)
{
delete []m_pStr;
m_pStr = 0;
}
}
CMyString& operator=(const CMyString &Str)
{
if (this == &Str) return *this;
delete []m_pStr;
m_pStr = 0;
m_pStr = new char[strlen(Str.m_pStr)+1];
strcpy_s(m_pStr,strlen(Str.m_pStr)+1,Str.m_pStr);
return *this;
}
CMyString& operator=(const char *pStr)
{
if (!pStr)
{
m_pStr = new char[2];
m_pStr[0] = 0;
m_pStr[1] = 0;
return;
}
delete []m_pStr;
m_pStr = 0;
m_pStr = new char[strlen(pStr)+1];
strcpy_s(m_pStr,strlen(pStr)+1,pStr);
return *this;
}
CMyString& operator=(const char str)
{
delete []m_pStr;
m_pStr = 0;
m_pStr = new char[2];
m_pStr[0] = str;
m_pStr[1] = 0;
return *this;
}
char* operator+(const CMyString & Str)
{
char *pTmp = 0 ;
pTmp = new char[strlen(this->m_pStr) + strlen(Str.m_pStr)+1];
strcpy_s(pTmp,strlen(this->m_pStr) + strlen(Str.m_pStr)+1 , this->m_pStr );
strcat_s(pTmp,strlen(this->m_pStr) + strlen(Str.m_pStr)+1 , Str.m_pStr );
return pTmp;
}
CMyString& operator+=(const CMyString &Str)
{
char *pTmp = 0;
pTmp = new char[strlen(this->m_pStr)+strlen(Str.m_pStr)+1];
strcpy_s(pTmp,strlen(this->m_pStr)+strlen(Str.m_pStr)+1,this->m_pStr);
strcat_s(pTmp,strlen(this->m_pStr)+strlen(Str.m_pStr)+1,Str.m_pStr);
delete []m_pStr;
m_pStr = 0;
m_pStr = new char[strlen(pTmp)+1];
strcpy_s(m_pStr,strlen(pTmp)+1,pTmp);
delete []pTmp;
pTmp = 0;
return *this;
}
CMyString &operator+=(const char *pStr)
{
if (!pStr)
{
return *this;
}
char *pTmp = 0;
pTmp = new char[strlen(this->m_pStr)+strlen(pStr)+1];
strcpy_s(pTmp,strlen(this->m_pStr)+strlen(pStr)+1,this->m_pStr);
strcat_s(pTmp,strlen(this->m_pStr)+strlen(pStr)+1,pStr);
delete []m_pStr;
m_pStr = 0;
m_pStr = new char[strlen(pTmp)+1];
strcpy_s(m_pStr,strlen(pTmp)+1,pTmp);
return *this;
}
CMyString & operator+=(char str)
{
char cTmp[2];
cTmp[0] = str;
cTmp[1] = 0;
(*this) += cTmp;
return *this;
}
friend ostream &operator<<(ostream& out,CMyString &Str);
friend istream &operator>>(istream& in,CMyString &Str);
};
ostream &operator<<(ostream& out,CMyString &Str)
{
out<<"CMyString: "<<Str.m_pStr;
return out;
}
istream &operator>>(istream& in,CMyString &Str)
{
in>>Str.m_pStr;
return in;
}
int main()
{
CMyString Str("123");
CMyString Str1("789");
Str = "12345";
Str += Str1;
cout << Str+ Str1<< endl;
cout << Str <<endl;
system("pause");
return 0;
}
可以看出,各函数都具有相同的思想。