对于一个类,本身应该有四个默认函数:
1.默认构造
2.默认析构
3.拷贝构造
4、默认赋值运算符operator= (赋值运算符重载函数)
赋值运算符重载函数与拷贝构造函数一样,都是对类的属性进行值拷贝。
#include<iostream>
using namespace std;
class Person
{
friend ostream& operator<<(ostream& cout, Person p);
public:
Person(){}
Person(int a) {
this->A = a;
}
//拷贝构造
Person(const Person& p) {
this->A = p.A;
}
//赋值
void operator=(const Person &p) {
this->A = p.A;
}
private:
int A;
};
//重载左移运算符
ostream& operator<<(ostream &cout,Person p) {
cout << "A=" << p.A;
return cout;
}
void test01()
{
Person p1(0);
Person p2(1);
p2 = p1; //赋值
Person p3(p1); //拷贝构造
cout << "p1=" << p1 << endl;
cout << "p2=" << p2 << endl;
cout << "p3=" << p3 << endl;
}
int main()
{
test01();
return 0;
}
但是两个函数却有些许不同
区别:
拷贝构造函数和赋值运算符的行为比较相似,都是将一个对象的值复制给另一个对象;但是其结果却有些不同,拷贝构造函数使用传入对象的值生成一个新的对象的实例,而赋值运算符是将对象的值复制给一个已经存在的实例。这种区别从两者的名字也可以很轻易的分辨出来,拷贝构造函数也是一种构造函数,那么它的功能就是创建一个新的对象实例;赋值运算符是执行某种运算,将一个对象的值复制给另一个对象(已经存在的)。调用的是拷贝构造函数还是赋值运算符,主要是看是否有新的对象实例产生。如果产生了新的对象实例,那调用的就是拷贝构造函数;如果没有,那就是对已有的对象赋值,调用的是赋值运算符。
另外,赋值运算符重载函数有一个特性,那就是可以有返回值。因此可以同时对多个类进行赋值。
代码:
#include<iostream>
using namespace std;
class Person
{
friend ostream& operator<<(ostream& cout, Person p);
public:
Person(){}
Person(int a) {
this->A = a;
}
//拷贝构造
Person(const Person& p) {
this->A = p.A;
}
//赋值
Person& operator=(const Person &p) {
this->A = p.A;
return *this; //可以有返回值
}
private:
int A;
};
//重载左移运算符
ostream& operator<<(ostream &cout,Person p) {
cout << "A=" << p.A;
return cout;
}
void test01()
{
Person p1(0);
Person p2(1);
Person pp(2);
p2 = p1 = pp; //可以对多个类同时进行赋值
Person p3(p1); //拷贝构造
cout << "p1=" << p1 << endl;
cout << "p2=" << p2 << endl;
cout << "p3=" << p3 << endl;
cout << "pp=" << pp << endl;
}
int main()
{
test01();
return 0;
}
同拷贝构造函数一样,赋值运算符重载函数也会发生深浅拷贝问题。在类与类之间进行赋值时,发生浅拷贝问题的时候,也要进行深拷贝。不过在进行深拷贝操作的时候,会略有不同。
代码:
#include<iostream>
using namespace std;
class Person
{
friend ostream& operator<<(ostream& cout, Person p);
public:
Person(){}
Person(int a) {
this->A = new int(a);
}
//拷贝构造
Person(const Person& p) {
//this->A = p.A; //浅拷贝
//不需要释放内存
//深拷贝
A = new int(*p.A);
}
//赋值
Person& operator=(const Person &p) {
//this->A = p.A; //浅拷贝
if (this->A != NULL) { //应该先判断是否有属性在堆区,如果有先释放干净,然后再深拷贝
delete A;
A = NULL;
}
//深拷贝
A = new int(*p.A);
return *this; //可以有返回值
}
~Person() { //堆区数据手动开辟,需要手动释放,因此需要调用析构函数。此时发生浅拷贝问题
if (A != NULL) {
delete A;
A = NULL;
}
}
private:
int *A;
};
//重载左移运算符
ostream& operator<<(ostream &cout,Person p) {
cout << "A=" << *p.A;
return cout;
}
void test01()
{
Person p1(0);
Person p2(1);
Person pp(2);
p2 = p1 = pp; //可以对多个类同时进行赋值
Person p3(p1); //拷贝构造
cout << "p1=" << p1 << endl;
cout << "p2=" << p2 << endl;
cout << "p3=" << p3 << endl;
cout << "pp=" << pp << endl;
}
int main()
{
test01();
return 0;
}
可以看到,在赋值运算符重载函数进行深拷贝之前,我们要先进行清理内存操作,再进行深拷贝。和拷贝构造函数创建新对象进行拷贝不同,赋值运算符是执行某种运算,将一个对象的值复制给将一个对象的值复制给另一个对象(已经存在的),因此我们要对已有的对象属性进行赋值,必须先清理掉已有的对象属性再赋值,这样就不会造成内存泄漏。