string函数的面试题分析
string函数的简单实现如下
class CMyString { public: CMyString(const char*str)//构造函数 { if (str == NULL)//若传入一个空字符串则直接开辟一个‘\0’的空间 { m_pData = new char[1]; m_pData = '\0'; } else { int length = strlen(str); m_pData = new char[length + 1];//开辟空间 strcpy(m_pData,str);//字符串拷贝 } } CMyString(const CMyString&c) { int length = strlen(c.m_pData); m_pData = new char[length + 1]; strcpy(m_pData, c.m_pData); } ~CMyString(void) { delete[] m_pData; } private: char*m_pData; };
对于赋值运算符函数,重点在于
◆1.函数返回值:函数返回值应该是该类型的引用,只有返回引用才可以实现连续赋值(str1=str2=str3)。同时,在函数结束时返回实例自身的引用,即*this
◆2.函数参数:函数参数因设置为常量引用,若传入参数为实例而不是引用会再调用一次拷贝构造函数构造一个无名参数,耗时费空间。const使传入的参数不会被改变
◆3.是否释放自身已有内存:若不在分配新的空间之前释放空间,会导致内存泄漏
◆4.自赋值判断:判断当前传入的实例和原*this指针指向实例是不是同一个,若是一个,则不能进行自赋值(在赋值结束释放原有空间时,新传入参数的内存也被释放,造成赋值失败)
程序1.0:
考虑到以上几点,写出如下
CMyString&operator=(const CMyString&c) { if (this != &c)//检查自赋值,若存在自赋值,在析构的时候会被析构两次,出现错误 { delete[]m_pData;//释放原有内存资源 int length = strlen(c.m_pData); m_pData = new char[length + 1];//分配新内存 strcpy(m_pData, c.m_pData);//复制 } return *this;//返回本对象的引用(不用再临时拷贝一份) }
程序2.0
在考虑以上几点的同时,考虑异常安全性的解法
CMyString&operator=(const CMyString&c) { if (this != &c) { CMyString StrTmp(c); char *tmp = StrTmp.m_pData; StrTmp.m_pData = m_pData; m_pData = tmp; } return *this; }
在这个新函数中,我们只要创建一个临时的实例,把这个实例的m_pData和临时创建的实例的m_pData做交换。由于新创建的实例是临时变量,在出了作用域后自动调用析构函数析构,释放零时实例的内存(这样就释放了这个实例的m_pData指向的内存)
在构造函数中,用new开辟空间,若开辟失败则抛出bad_alloc异常,由于我们在构造时还未修改原来实例的状态,故实例的状态还是有效的,故保证了异常安全性。