C++之 构造函数 拷贝构造函数 析构函数 赋值操作

来源:互联网 发布:飞鱼网络电视看不了 编辑:程序博客网 时间:2024/04/28 00:34

编译器默认给C++生成四个成员函数,分别是构造函数、拷贝构造函数、析构函数和赋值操作。对这四个函数的考察经常在很多IT公司的笔试或者面试题目中出现。本文以mystring类为例,讲述默认生成的四个成员函数的基本操作。

mystring类的声明

#include <iostream>#include <string>using namespace std;class mystring{public:mystring(char* str=NULL);//构造函数mystring(const mystring &obString);//拷贝构造函数~mystring();//析构函数mystring& operator=(const mystring &obString);                //赋值操作符void show(){cout<<m_pData<<endl;}private: char* m_pData;};

构造函数

mystring::mystring(char* str){if (NULL == str){m_pData = new char[1];m_pData[0]='\0';}else{m_pData = new char[strlen(str)+1];strcpy(m_pData, str);}}

析构函数,默认的析构函数不能删除new运算符在堆中分配的对象或对象成员。如果对象中成员占用的内存是在堆中分配的,必须定义析构函数,然后显示的使用delete运算符来释放内存。

mystring::~mystring(){delete [] m_pData;}

如果不显示的编写类的拷贝构造函数和赋值操作符,编译器将以“位拷贝”的方式自动生成缺省的函数,倘若类中含有指向堆上申请的内存的指针,那么这两个缺省的成员函数就隐含了错误。

如string类定义两个变量 A和B,A.m_data = "hello", B.m_data="world",若将A = B,则将造成三个错误,① A.m_data对应的内存未被释放,② 此时A.m_data和B.m_data指向同一个内存块,A和B任何一方变动都会影响另一方,③ B中的m_data被释放了两次,导致运行时程序崩溃。因此需要我们自定义拷贝构造函数。

拷贝构造函数,其参数是const对象的引用。

mystring::mystring(const mystring &obString){m_pData = new char[strlen(obString.m_pData)+1];strcpy(m_pData, obString.m_pData);}

赋值操作符函数

mystring& mystring::operator=(const mystring &obString){if (this == &obString){//检查是否是自赋值return *this;}delete[] m_pData;//删除原来申请的内存空间m_pData = new char[strlen(obString.m_pData)+1];strcpy(m_pData, obString.m_pData);return *this;}

一直以为上面的赋值操作符函数是一个标准的写法,在《名企面试官精讲典型编程题》中,上面的赋值操作符函数只是初级程序员的写法。

上面的赋值操作函数,若在分配内存之前先用delete释放了m_pData的内存。如果此时内存不足导致new char抛出异常,m_pData将是一个空指针,这样非常容易导致程序崩溃。也就是说一旦在赋值运算符函数内部抛出一个异常,mystring的实例不再保持有效状态,这就违背了C++异常安全性的原则(Exception Safety)

有两个方法可以解决上述问题,① 内存分配成功之后再删除m_pData对应的内存,② (更好的方法)创建一个临时对象,再交换临时对象和原来的对象,代码如下。

mystring& mystring::operator=(const mystring &obString){if (this != &obString){mystring obTmp(obString);char* pTmp = obTmp.m_pData;obTmp.m_pData = m_pData;m_pData = pTmp;}return *this;}

上述代码中,先创建一个临时对象obTmp,接着把obTmp.m_pData 和自身的 m_pData交换,由于obTmp是一个局部变量,当程序执行完if内的语句之后,程序会自动调用obTmp的析构函数,把obTmp.m_pData指向的内存释放掉。在新的代码中,我们在mystring的构造函数里用new分配内存,如果出现因内存不足抛出bad_alloc等异常,以前对象的状态还没有修改,对象的状态还是有效的,这也就保证了异常安全性

测试

int main(){char *p = "ABCDE";mystring B(p);//验证构造函数B.show();mystring AD = B;//验证赋值操作符AD.show();mystring C(B);//验证拷贝构造函数C.show();return 0;}

参考

《名企面试官精讲典型编程题》




0 0
原创粉丝点击