本文转自:http://zhedahht.blog.163.com/blog/static/25411174200741543224391/
问题:给出如下CMyString的声明,要求为该类型添加赋值运算符函数。
class CMyString
{
public:
CMyString(char* pData = NULL);
CMyString(const CMyString& str);
~CMyString(void);
private:
char* m_pData;
};
当面试官要求应聘者定义一个复制运算符函数时,他会关注如下几点:
· 是否把返回值的类型声明为该类型的引用,并在函数结束前返回实例自身(即*this)的引用?只有返回一个引用,才可以允许连续赋值。否则如果函数的返回值是void,假设有三个CMyString的对象,str1、str2和str3,在程序中语句str1=str2=str3将不能通过编译。
· 是否把传入的参数的类型声明为常量引用?如果传入的参数不是引用而是实例,那么从形参到实参会调用一次构造拷贝函数。把参数申明为引用可以避免这样的无谓消耗,能提高代码的效率。同时,我们在赋值运算符函数内是不会改变传入的实例的状态的,因此应该为传入的引用参数加上const关键字。
· 是否记得释放实例自身已有的内存?如果忘了在分配新内存之前释放自身已有的空间,将出现内存泄露。
· 是否判断传入的参数是不是和当前的实例(*this)是不是同一个实例?如果是同一个,则不进行赋值操作,直接返回。如果事先不判断,就进行赋值,那么在释放实例自身的内存的时候就会导致严重的问题:当*this和传入的参数是同一个实例时,那么一旦释放了自身的内存,传入的参数的内存也同时被释放了,因此再也找不到需要赋值的内容了。
下面是完整的代码:
/***********
CMyString.h
************/
class CMyString
{
public:
//默认构造函数
CMyString();
//带参数的构造函数
CMyString(const char *str);
//拷贝构造函数
CMyString(const CMyString& str);
//赋值构造函数
CMyString& operator= (const CMyString& str);
~CMyString();
void Print();
private:
char *m_pData;
};
/***********
CMyString.cpp
************/
#include <iostream>
#include <stdio.h>
#include <string.h>
#include "CMyString.h"
using namespace std;
//初始给字符串对象分配一个空间,存储'\0'
CMyString::CMyString()
{
m_pData = new char[1];
if(NULL == m_pData)
{
printf("No more memory\n");
m_pData = NULL;
}
else
m_pData[0] = '\0';
}
//拷贝形参字符串的内容的内容
CMyString::CMyString(const char *str)
{
if(NULL == str)
{
m_pData = new char[1];
if(NULL == m_pData)
{
printf("No more memory\n");
}
else
m_pData[0] = '\0';
}
else
{
int len = strlen(str);
m_pData = new char[len + 1];
if(NULL == m_pData)
{
printf("No more memory\n");
}
else
{
strcpy(m_pData,str);
m_pData[len] = '\0';
}
}
}
/*复制构造函数,传入的参数是常引用,这样就避免了传参时,从形参到实参会调用一次构造拷贝函数,
因为不改变参数值得内容,所以声明为const引用*/
CMyString::CMyString(const CMyString& str)
{
if(NULL == str.m_pData)
{
m_pData = new char[1];
if(NULL == m_pData)
{
printf("No more memory\n");
}
else
m_pData[0] = '\0';
}
else
{
int len = strlen(str.m_pData);
m_pData = new char[len + 1];
if(NULL == m_pData)
{
printf("No more memory\n");
}
else
{
strcpy(m_pData,str.m_pData);
m_pData[len] = '\0';
}
}
}
//析构函数,释放空间后,置成员指针变量m_pData为空
CMyString::~CMyString()
{
if(NULL != m_pData)
{
delete m_pData;
m_pData = NULL;
}
}
/*1、返回值的类型声明为该类型的引用,并在函数结束前返回实例自身(即*this)的引用,这样才允许连续赋值
2、传入的参数的类型声明为常量引用,如果传入的参数不是引用而是实例,那么从形参到实参会调用一次构造拷贝函数,
把参数申明为引用可以避免这样的无谓消耗,能提高代码的效率,
同时,我们在赋值运算符函数内是不会改变传入的实例的状态的,因此应该为传入的引用参数加上const关键字。
3、记得释放实例自身已有的内存,避免出现内存泄露
4、判断传入的参数是不是和当前的实例(*this)是不是同一个实例?如果是同一个,则不进行赋值操作,直接返回。
如果事先不判断,就进行赋值,那么在释放实例自身的内存的时候就会导致严重的问题:当*this和传入的参数是同一个实例时,
那么一旦释放了自身的内存,传入的参数的内存也同时被释放了,因此再也找不到需要赋值的内容了。*/
CMyString& CMyString::operator =(const CMyString &str)
{
if(this != &str)
{
int len = strlen(str.m_pData);
char *pStr = new char[len+1];
if(NULL == pStr)
{
printf("No more memory\n");
}
else
{
strcpy(pStr,str.m_pData);
//记得释放实例自身已有的内存,避免出现内存泄露
delete m_pData;
m_pData = pStr;
}
}
return *this;
}
void CMyString::Print()
{
if(NULL != m_pData)
printf("%s\n",m_pData);
}
/***********
main.cpp
************/
#include <iostream>
#include <stdio.h>
#include "CMyString.h"
int main()
{
//char str[] = "Hello World";
CMyString str1("Hello World5");
str1 = str1;
CMyString str2("Good Morning!");
CMyString str3 = str2 = str1;
str2.Print();
return 0;
}