题目
如下为类型CMyString
的声明,请为该类型添加赋值运算符函数。
class CMyString {
public :
CMyString(char * pData = NULL);
CMyString(const CMyString & str);
~CMyString(void);
private :
char * m_pData;
};
解析
首先,要思考一下类中已经声明的函数如何实现,当然这个过程可能没必要写出来。类CMyString
内部使用char *
存储信息,C语言中常用的几个字符串处理函数要熟悉,比如strlen()
和strcpy()
。
CMyString::CMyString(char * pData) {
// 函数的默认参数实现时不需要标明
if(pData == NULL) {
m_pData = new char[1];
m_pData[0] = '\0';
} else {
m_pData = new char[strlen(pData) + 1];
strcpy(m_pData, pData);
}
}
CMyString::CMyString(const CMyString & str) {
m_pData = new char[strlen(str.m_pData) + 1];
strcpy(m_pData, str.m_pData);
}
CMyString::~CMyString() {
delete [] m_pData;
}
解决这个问题需要关注一下几点:
- 赋值函数的返回值类型应声明为该类型的引用,从而允许连续赋值。
- 形参应声明为常量引用,提高代码效率。
- 赋值前释放实例自身已有的空间,避免内存泄露。
- 操作前判断传入的参数和当前的实例是否为同一实例。
解法1:经典解法,未考虑内存不足导致new char抛出异常。
CMyString & CMyString::operator = (const CMyString & str) {
if(this == &str) {
return *this;
}
delete [] m_pData;
m_pData = NULL;
m_pData = new char[strlen(str.m_pData) + 1];
strcpy(m_pData, str.m_pData);
return * this;
}
上面的代码考虑的问题已经很周到了,但是却没注意到内存不足导致new char
抛出异常,此时程序很容易崩溃。也就是说一旦在赋值运算符函数内部抛出一个异常,CMyString
的实例不再保持有效的状态,这就违背了异常安全性原则。
解法2:考虑异常安全性,分配内容成功后再释放原来的内容。
CMyString & CMyString::operator = (const CMyString & str) {
if(this == &str) {
return *this;
}
char * pTemp = new char[strlen(str.m_pData) + 1];
delete [] m_pData;
m_pData = NULL;
m_pData = pTemp;
strcpy(m_pData, str.m_pData);
return *this;
}
解法3:先创建一个临时实例,再交换临时实例和原来的实例。
CMyString & CMyString::operator = (const CMyString & str) {
if(this != &str) {
CMyString strTemp(str);
// 需要交换,因为对象的两个实例空间上不能有交集
char * pTemp = strTemp.m_pData;
strTemp.m_pData = m_pData;
m_pData = pTemp;
}
return * this;
}
上面的解法中,如果内存不足抛出异常,依然能够保证实例的状态还是有效的。
为了检验赋值效果,顺便写了几个测试函数,主要测试了普通赋值、连续赋值、赋值给自己等点。
void CMyString::print() {
printf("%s\n", m_pData);
}
void test1() {
// 普通赋值
printf("test1\n");
CMyString str1("hello, world.");
CMyString str2;
str2 = str1;
str1.print(); // hello, world.
str2.print(); // hello, world.
}
void test2() {
// 连续赋值
printf("test2\n");
CMyString str1("hello, world.");
CMyString str2, str3;
str3 = str2 = str1;
str3.print(); // hello, world.
}
void test3() {
// 赋值给自己
printf("test3\n");
CMyString str("hello, world.");
str = str;
str.print(); // hello, world.
}
小结
该题主要考察了C++的基础语法、内存泄露、异常安全性等问题,看似简单但考虑周到确实有一定难度的。
参考
- 剑指Offer——名企面试官精讲典型编程题 - 何海涛
(全文完)