1 . 0 Why 拷贝构造函数?
有时候需要用一个类对象初始化该类的另一个对象。设计一个拷贝构造函数(copy constructor)来实现一个类对象向该类的另一个对象作拷贝是通过依次拷贝每个非静态数据成员。
拷贝构造函数与普通的构造函数一样:
(1)如果没有自定义的拷贝构造函数则系统自动生成一个默认的拷贝构造函数;
(2)当采用=号赋值初始化 或 用对象作为参数实例化对象时系统会自动调用拷贝构造函数。
1 . 1 拷贝构造函数的分类
(1)浅拷贝:对于有指针成员的类,在拷贝构造函数中只是简单的赋值,使两个对象的指针成员指向同一内存,使用系统默认的拷贝构造函数,可能出现“指针悬挂”(同一内存空间被释放两次)。
(2)深拷贝:对于有指针成员的类,在拷贝构造函数中重新分配内存,并把参数对象的指针所指向的内容拷贝给该对象指针所指向的内存。
2 . 0 Why 赋值运算符重载?
运算符重载是针对新类型数据的实际需要对原有运算符进行的适当的改造,重载的功能应当与原有功能相类似。例如:类对象直接进行相互赋值、类对象之间之间进行加减运算等。
2 . 1 运算符函数形式:
<返回类型说明符> operator <运算符符号> (<参数表> )
{
<函数体>
}
2 . 2对于编写赋值运算符重载时注意事项
1. 是否把返回值的类型声明为该类型的引用,并在函数结束前返回
实例自身的引用(*this),只有返回引用,才可以允许连续赋值。
2. 是否把传入的参数的类型声明为常量引用。如果传入的参数不是
引用而是实例,那么从形参到实参会调用一次拷贝构造函数,增加了
无谓的消耗,降低代码的效率。
3. 是否释放实例自身已有的内存,如果忘记则会造成内存泄漏。
4. 是否判断传入的参数和当前的实例(*this)是不是同一个实例
。若是同一个,则不进行赋值,直接返回。如果事先不判断就进行赋
值,在释放内存的时候回导致严重的问题:由于释放自己的内存,找
不到需要赋值的内容。
根据以上分析,写出带有深拷贝构造和赋值运算符重载的简单例子,如下:
/*************************************************************************
> File Name: CMstring.h
> Author: HanRui
> Mail: xautofhmm@gmail.com
************************************************************************/
#ifndef _CMSTRING_H
#define _CMSTRING_H
#include <ostream>
using namespace std;
class CMyString
{
public:
CMyString(char * pData = NULL);
CMyString(const CMyString & str);
~CMyString(void);
CMyString & operator=(const CMyString &str);
friend ostream & operator<< (ostream & co, CMyString &p);
private:
char *m_pData;
};
#endif
/*************************************************************************
> File Name: CMyString.cpp
> Author: HanRui
> Mail: xautofhmm@gmail.com
************************************************************************/
#include<string.h>
#include<ostream>
#include<iostream>
#include "CMyString.h"
using namespace std;
#define NUM 100
CMyString::CMyString(char *pData)
{
cout << "CMyString(char *) func is call\n";
if(NULL == pData)
{
m_pData = NULL;
}
else
{
m_pData = new char[strlen(pData) + 1];
if(NULL == m_pData)
{
cout << "constructor create m_pData error\n" ;
m_pData = NULL;
}
strcpy(m_pData, pData);
}
}
CMyString::CMyString(const CMyString & str)
{
cout << "CMyString(const CMyString &) func is call\n";
if(str.m_pData == NULL)
{
m_pData = NULL;
}
else
{
m_pData = new char[strlen(str.m_pData) + 1];
if(NULL == m_pData)
{
cout << "CMyString(const CMyString &) error\n";
m_pData = NULL;
}
strcpy(m_pData, str.m_pData);
}
}
CMyString & CMyString::operator=(const CMyString &str)
{
cout << "operator= func is call\n";
if(this != &str)
{
CMyString strTmp(str);
char *pTmp = strTmp.m_pData;
strTmp.m_pData = this->m_pData;
this->m_pData = pTmp;
}
return *this;
}
CMyString::~CMyString()
{
cout << "~CMyString() func is call\n";
if(m_pData != NULL)
{
delete m_pData;
cout << "delete m_pData\n";
}
else
{
cout << "m_pData is NULL\n";
}
}
ostream & operator<<(ostream &co, CMyString &p)
{
co << p.m_pData;
return co;
}
/*************************************************************************
> File Name: main.cpp
> Author: HanRui
> Mail: xautofhmm@gmail.com
************************************************************************/
#include<iostream>
#include "CMyString.h"
using namespace std;
int main()
{
char ptemp[] = {"hello, world!"};
CMyString str1(ptemp);
CMyString str2(str1);
CMyString str3 = str2;
CMyString str4 = str3 = str2;
cout << "str1: " << str1 << endl;
cout << "str2: " << str2 << endl;
cout << "str3: " << str3 << endl;
cout << "str4: " << str4 << endl;
CMyString str5;
return 0;
}
3 . 0 运行结果(运行环境:Ubuntu g++)
[1]. 参考书籍《剑指offer》何海涛 著