C++中的operator=运算符重载

在编程时对赋值运算符的重载是有时需要进行考虑的部分,今天在这里对这一部分的内容进行一下记录。

首先是MyString类的定义部分:

#include

using namespace std;

class MyString
{
private:
	char* m_pData;
public:
	MyString(const char* pData = nullptr);
	MyString(const MyString& str);
	~MyString(void) {}
	MyString& operator=(const MyString & str);
	void Print() { cout << m_pData << endl; }
};

MyString::MyString(const char * pData)
{
	m_pData = new char[strlen(pData) + 1];
	strcpy_s(m_pData, strlen(pData) + 1, pData);
}

MyString::MyString(const MyString & str)
{
	m_pData = new char[strlen(str.m_pData) + 1];
	strcpy_s(m_pData, strlen(str.m_pData), str.m_pData);
}

MyString::~MyString(void)
{
	delete[] m_pData;
}

在运算符重载中,我们有几个地方需要特殊的注意:

首先,operator=的运算符重载最好将函数的返回类型定义为类的引用,即为MyString &,尽管C++允许我们将返回类型定义为值类型或void类型,但在某些情况下会产生一定的问题并且在效率上有一定缺陷。首先,函数如果返回值是值类型,则需要在返回时再次调用一次拷贝构造,效率较低;而如果是进行连续赋值,例如a = b = c时,首先会对return的值类型对象进行一次拷贝构造,然后将这个新的对象返回,所以执行a = b后得到的是一个右值,不能作为左值,就会报错。而如果使用void作为返回值,在大部分情况不会出现问题,但是在连续赋值,如a = b = c的时候,除非b = c返回的是a的同类型的东西(虽然一般都是),连续赋值就会报错,另一种情况是,如果在代码

mytype obj;
while ((obj = read_obj(cin)) != END_OBJ) {
    ...
}

这种地方,void是不能使用的。因此,使用引用作为返回值是最好的选择。

第二点需要注意的是,传入的参数最好声明为const 类型的引用,能够最大可能的提高代码效率。

第三点是,在进入函数后,要首先判断传入参数和当前的实例是否为同一个实例,否则在释放当前实例的时候,传入参数的值同样被释放,就会造成尴尬的局面。

第四点是,在开辟新的空间放置新内容时,要首先释放掉原有空间,保证没有内存泄漏发生。

MyString& MyString::operator=(const MyString & str)
{
	if (this == &str)
	{
		return *this;
	}
	delete[] m_pData;
	m_pData = nullptr;
	m_pData = new char[strlen(str.m_pData) + 1];
	strcpy_s(m_pData, strlen(str.m_pData), str.m_pData);
	return *this;
}

int main()
{
	char c;
	const char* c1 = "aaa";
	const char* c2 = "bbb";
	const char* c3 = "ccc";
	MyString* s1 = new MyString(c1);
	MyString* s2 = new MyString(c2);
	MyString* s3 = new MyString(c3);
	s1 = s2 = s3;
	s2->Print();
	s3->Print();
	cin >> c;
	return 0;
}

而在考虑了代码的安全性后,我们可以将程序修改为如下所示:

MyString& MyString::operator=(const MyString & str)
{
	if (this != &str)
	{
		MyString tmpStr(str);
		char* tmp = tmpStr.m_pData;
		tmpStr.m_pData = m_pData;
		m_pData = tmp;
	}
	return *this;
}

在这一段代码中,我们可以防止在new时,如果出现内存不足分配失败的错误时,防止原有对象的值被清空。在if作用域内部定义tmpStr,并将其指针指向目标对象所指内存地址,再声明char*指针记录目标地址,然后将tmpStr指针指向当前指针后不去管它,这样就会在离开if作用域后自动析构,释放掉原地址内存,而将当前对象的指针指向新声明的char*指针指向的目标内存。这样如果在构造函数中的new中发现报错时,还没有释放原有内存,就防止了内存丢失的发生。

你可能感兴趣的:(C++)