C++中两个对象赋值有两种方式
- 深拷贝和浅拷贝
- 赋值运算符重载函数(operator =)
1、深拷贝和浅拷贝
在未定义拷贝构造函数时,系统会调用默认的拷贝构造函数---即浅拷贝,它能够完成成员的一一复制。当数据成员中没有指针时,浅拷贝是可行的;当数据成员中有指针时,如果采用简单的浅拷贝,则两个类的两个指针都将指向同一个地址;当生命周期结束时,会调用两次析构函数,而指针成员变量只有一块空间,释放一次后,另一个指针所指向的空间就不存在了,从而导致程序悬挂;
总结:当数据成员中有指针时,必须要用深拷贝;
解决方案:自定义拷贝函数,形参为对象的引用
#include <iostream>
#include <stdio.h>
#include <set>
#include <string>
using namespace std;
class ClassA
{
public:
//调用无参构造函数
ClassA()
{
}
ClassA(const char* pszInputStr)
{
pszTestStr = new char[strlen(pszInputStr)+1];
strncpy(pszTestStr, pszInputStr, strlen(pszInputStr) + 1);
}
赋值运算符重载函数
//ClassA& operator=(const ClassA& cls)
//{
// //避免自赋值
// if (this != &cls)
// {
// //避免内存泄露
// if (pszTestStr != NULL)
// {
// delete pszTestStr;
// pszTestStr = NULL;
// }
// pszTestStr = new char[strlen(cls.pszTestStr) + 1];
// strncpy(pszTestStr, cls.pszTestStr, strlen(cls.pszTestStr) + 1);
// }
// return *this;
//}
//调用拷贝构造函数
ClassA(const ClassA& cls)
{
//避免自赋值
if (this != &cls)
{
//避免内存泄露
if (pszTestStr != NULL)
{
delete pszTestStr;
pszTestStr = NULL;
}
pszTestStr = new char[strlen(cls.pszTestStr) + 1];
strncpy(pszTestStr, cls.pszTestStr, strlen(cls.pszTestStr) + 1);
}
}
virtual ~ClassA()
{
delete pszTestStr;
}
public:
char* pszTestStr;
};
int main()
{
ClassA obj1("liitdar");
ClassA obj2=obj1; //调用拷贝构造函数
//ClassA obj2; //调用无参构造函数
//obj2 = obj1; //调用赋值预算符
cout << "obj2.pszTestStr is: " << obj2.pszTestStr << endl;
cout << "addr(obj1.pszTestStr) is: " << &obj1.pszTestStr << endl;
cout << "addr(obj2.pszTestStr) is: " << &obj2.pszTestStr << endl;
system("pause");
return 0;
}
2、赋值运算符重载函数(operator=)
当类中不包含指针变量时,使用默认的赋值运算符是没有问题的
#include <iostream>
using namespace std;
class ClassA
{
public:
int a;
int b;
int c;
};
int main()
{
ClassA obj1;
obj1.a = 1;
obj1.b = 2;
obj1.c = 3;
ClassA obj2;
obj2 = obj1;
cout << "obj2.a is: " << obj2.a << endl;
return 0;
}
当类中包含指针变量时,调用析构函数时,由于重复释放了同一块内存,会导致程序崩溃,我们就不能使用默认的重载赋值运算符了,需要我们重写。执行到最后关闭程序时会报错。
#include <iostream>
#include <string.h>
using namespace std;
class ClassA
{
public:
ClassA()
{
}
ClassA(const char* pszInputStr)
{
pszTestStr = new char[strlen(pszInputStr) + 1];
strncpy(pszTestStr, pszInputStr, strlen(pszInputStr) + 1);
}
virtual ~ClassA()
{
delete pszTestStr;
}
public:
char* pszTestStr;
};
int main()
{
ClassA obj1("liitdar");
ClassA obj2;
obj2 = obj1;
cout << "obj2.pszTestStr is: " << obj2.pszTestStr << endl;
cout << "addr(obj1.pszTestStr) is: " << &obj1.pszTestStr << endl;
cout << "addr(obj2.pszTestStr) is: " << &obj2.pszTestStr << endl;
return 0;
}
调用赋值运算符重载函数(operator=)
#include <iostream>
#include <stdio.h>
#include <set>
#include <string>
using namespace std;
class ClassA
{
public:
//调用无参构造函数
ClassA()
{
}
ClassA(const char* pszInputStr)
{
pszTestStr = new char[strlen(pszInputStr)+1];
strncpy(pszTestStr, pszInputStr, strlen(pszInputStr) + 1);
}
//赋值运算符重载函数
ClassA& operator=(const ClassA& cls)
{
//避免自赋值
if (this != &cls)
{
//避免内存泄露
if (pszTestStr != NULL)
{
delete pszTestStr;
pszTestStr = NULL;
}
pszTestStr = new char[strlen(cls.pszTestStr) + 1];
strncpy(pszTestStr, cls.pszTestStr, strlen(cls.pszTestStr) + 1);
}
return *this;
}
调用拷贝构造函数
//ClassA(const ClassA& cls)
//{
// //避免自赋值
// if (this != &cls)
// {
// //避免内存泄露
// if (pszTestStr != NULL)
// {
// delete pszTestStr;
// pszTestStr = NULL;
// }
// pszTestStr = new char[strlen(cls.pszTestStr) + 1];
// strncpy(pszTestStr, cls.pszTestStr, strlen(cls.pszTestStr) + 1);
// }
//}
virtual ~ClassA()
{
delete pszTestStr;
}
public:
char* pszTestStr;
};
int main()
{
ClassA obj1("liitdar");
//ClassA obj2=obj1; //调用拷贝构造函数
ClassA obj2; //调用无参构造函数
obj2 = obj1; //调用赋值预算符
cout << "obj2.pszTestStr is: " << obj2.pszTestStr << endl;
cout << "addr(obj1.pszTestStr) is: " << &obj1.pszTestStr << endl;
cout << "addr(obj2.pszTestStr) is: " << &obj2.pszTestStr << endl;
system("pause");
return 0;
}
其中需要注意的是:
第一种方式,调用拷贝构造函数方式
//ClassA obj2=obj1; //调用拷贝构造函数
第二种方式,调用赋值运算符方式
ClassA obj2; //调用无参构造函数
obj2 = obj1; //调用赋值预算符
- 当为一个类的对象赋值(可以用本类的对象为其赋值,也可以用其他类型的值为其赋值)时,该对象(如本例的obj2)会调用该类的赋值运算符重载函数,进行具体的赋值操作。如上述代码的"obj2 = obj1",用obj1为obj2赋值,则会由obj2调用ClassA类的赋值运算符重载函数;
- 下方语句和语句"ClassA obj2 = obj1;" 在调用上还是有区别的:前者第一句是对象obj2的声明和定义,调用classA的无参构造函数,所以“obj2 =obj1”是在对象obj2已经存在的情况下,用obj1来为obj2进行赋值,调用的是赋值运算符重载函数; 而后者,是用obj1来初始化obj2,调用的是拷贝构造函数。拷贝构造函数的语句样式为"ClassA(const Class& cls)",在本文介绍拷贝构造函数也有代码体现。
ClassA obj2; obj2 = obj1;