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

编译器默认给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;
}

参考

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




  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值