【剑指offer】面试题一:赋值运算符函数

题目:如下为类型 CMyString 的声明,请为该类型添加赋值运算符函数。

类的声明如下:

 1 class CMyString
 2 {
 3 public:
 4     CMyString(char *pData = NULL);
 5     CMyString(const CMyString &str);
 6     ~CMyString();
 7     
 8 private:
 9     char *m_pData;
10 };

关于赋值运算符函数,我们应该从以下几个方面考虑:

1、返回值:首先我们应该把返回值的类型声明为该类型的引用,并在函数结束前返回本身的引用。只有返回一个引用才能允许连续赋值,如 str1 = str2 = str3;

2、参数:其次我们应该把传入的参数的类型声明为常量引用。

  传入的引用是为了避免从形参到实参调用的一次复制构造函数;这也就是传值与传引用的效率问题。

  传入的为常量从语义的角度出发,我们在赋值运算函数内并不希望改变传入的实例的状态,因此,应该为传入的引用参数加上 const 关键字。

3、内存:我们需要实例释放自身已有的内存,如果我们王子在分配新内存之前释放掉自身已有的内存,就会造成内存泄露;

4、自身赋值问题:最后我们需要判断传入的参数和当前的实例(*this)是否为同一个实例。如果为同一个,则不进行赋值操作。因为,赋值之前需要释放已有的内存,如果为同一实例,那么传入的参数的内存在当前实例释放内存的同时也被释放了,这样就无法找到需要赋值的内容了。

 

一、基本的解法:

  1、判断传入的参数是否为当前的实例本身;

  2、就是释放掉当前实例(*this)的内存;

  3、进行赋值;

  4、返回实例(*this);

 1 CMyString& CMyString::operator=(const CMyString &str)
 2 {
 3     if(this == &str)
 4         return *this;
 5 
 6     delete []m_pData;
 7     m_pData = NULL;
 8     m_pData = new char[strlen(str.m_pData)+1];
 9     strcpy(m_pData, str.m_pData);
10 
11     return *this;
12 }
View Code

 

二、考虑异常安全性的解法:

解法一存在安全问题,考虑如下问题:

我们在分配内存之前先用 delete 释放了实例 m_pData 的内存。 如果此时内存不足导致 new char 抛出异常,这样 m_pData 将会是一个空指针,这样非常容易导致程序崩溃。

在这里,我们用借用一个临时变量来解决上述问题。具体步骤为:

先用传入的参数创建一个临时变量tmp,接着用该临时变量tmp 与实例自身的 m_pData 数据做交换。由于tmp为一个局部变量,该段程序运行结束时就是自动调用tmp的析构函数,把tmp的内存释放掉。

代码如下:

 1 CMyString& CMyString::operator=(const CMyString &str)
 2 {
 3     if(this != &str)
 4     {
 5         CMyString tmp(str);
 6 
 7         char *pTmp = tmp.m_pData;
 8         tmp.m_pData = m_pData;
 9         m_pData = pTmp;
10     }
11     return *this;
12 }
View Code

 

三、完整的CMyString类的实现及测试代码:

CMyString.hpp文件

 1 #ifndef CMYSTRING
 2 #define CMYSTRING 
 3 
 4 #include <string.h>
 5 
 6 class CMyString
 7 {
 8 public:
 9     CMyString();
10     CMyString(char *pData);
11     CMyString(const CMyString &str);
12     CMyString& operator=(const CMyString &str);
13     ~CMyString();
14 
15 private:
16     char *m_pData;
17 };
18 
19 CMyString::CMyString()
20 :m_pData(new char[1])
21 {
22     m_pData = '\0';
23 }
24 
25 CMyString::CMyString(char *pData)
26  :m_pData(new char[strlen(pData)+1])
27 {
28     ::strcpy(m_pData, pData);
29 }
30 
31 CMyString::CMyString(const CMyString &str)
32  :m_pData(new char[strlen(str.m_pData)+1])
33 {
34     ::strcpy(m_pData, str.m_pData);
35 }
36 
37 CMyString& CMyString::operator=(const CMyString &str)
38 {
39     if(&str != this)
40     {
41         CMyString tmp(str);
42 
43         char *cTmp = tmp.m_pData;
44         tmp.m_pData = m_pData;
45         m_pData = cTmp;
46     }
47     return *this;
48 }
49 
50 CMyString::~CMyString()
51 {
52     delete m_pData;
53 }
54 
55 #endif /*CMYSTRING*/
View Code

测试代码main.cpp:

 1 #include "CMyString.hpp"
 2 #include <iostream>
 3 #include <string>
 4 
 5 using namespace std;
 6 
 7 int main(int argc, char const *argv[])
 8 {
 9     CMyString myString;
10 
11     char tmp[] = "helloworld";    
12     CMyString myString2(tmp);
13 
14     myString = myString2;
15 
16     CMyString m3(myString);
17     
18     return 0;
19 }
View Code 

编译与执行:

1 g++ -o mystring main.cpp
2 ./mystring

 

引申:

题目:一个空类,编辑器默认为其生成几个函数?

类的声明如下:

class Empty
{

};

在该类实例化以后,一般编辑器会为其自动生成 默认构造函数、赋值构造函数、赋值运算符函数和析构函数 四个函数,

具体如下:

class Empty
{
public:
    Empty();
    Empty(const Empty &e);
    Empty& operator=(const Empty &e);
    ~Empty();
};
View Code
posted @ 2015-06-09 14:31  Stephen_Hsu  阅读(265)  评论(0编辑  收藏  举报