32.赋值运算符重载

1.赋值(=)运算符重载

  赋值符常常初学者的混淆。这是毫无疑问的,因为’=’在编程中是最基本的运算符,可以进行赋值操作,也能引起拷贝构造函数的调用。

class Person{
	friend ostream& operator<<(ostream& os,const Person& person){
		os << "ID:" << person.mID << " Age:" << person.mAge << endl;
		return os;
	}
public:
	Person(int id,int age){
		this->mID = id;
		this->mAge = age;
	}
	//重载赋值运算符
	Person& operator=(const Person& person){
		this->mID = person.mID;
		this->mAge = person.mAge;
		return *this;
	}
private:
	int mID;
	int mAge;
};

//1. =号混淆的地方
void test01(){
	Person person1(10, 20);
	Person person2 = person1; //调用拷贝构造
	//如果一个对象还没有被创建,则必须初始化,也就是调用构造函数
	//上述例子由于person2还没有初始化,所以会调用构造函数
	//由于person2是从已有的person1来创建的,所以只有一个选择
	//就是调用拷贝构造函数
	person2 = person1; //调用operator=函数
	//由于person2已经创建,不需要再调用构造函数,这时候调用的是重载的赋值运算符
}
//2. 赋值重载案例
void test02(){
	Person person1(20, 20);
	Person person2(30, 30);
	cout << "person1:" << person1;
	cout << "person2:" << person2;
	person2 = person1;
	cout << "person2:" << person2;
}
//常见错误,当准备给两个相同对象赋值时,应该首先检查一下这个对象是否对自身赋值了
//对于本例来讲,无论如何执行这些赋值运算都是无害的,但如果对类的实现进行修改,那么将会出现差异;
//3. 类中指针
class Person2{
	friend ostream& operator<<(ostream& os, const Person2& person){
		os << "Name:" << person.pName << " ID:" << person.mID << " Age:" << person.mAge << endl;
		return os;
	}
public:
	Person2(char* name,int id, int age){
		this->pName = new char[strlen(name) + 1];
		strcpy(this->pName, name);
		this->mID = id;
		this->mAge = age;
	}
#if 1
	//重载赋值运算符
	Person2& operator=(const Person2& person){

		//注意:由于当前对象已经创建完毕,那么就有可能pName指向堆内存
		//这个时候如果直接赋值,会导致内存没有及时释放
		if (this->pName != NULL){
			delete[] this->pName;
		}

		this->pName = new char[strlen(person.pName) + 1];
		strcpy(this->pName,person.pName);
		this->mID = person.mID;
		this->mAge = person.mAge;
		return *this;
	}
#endif
	//析构函数
	~Person2(){
		if (this->pName != NULL){
			delete[] this->pName;
		}
	}
private:
	char* pName;
	int mID;
	int mAge;
};

void test03(){
	Person2 person1("John",20, 20);
	Person2 person2("Edward",30, 30);
	cout << "person1:" << person1;
	cout << "person2:" << person2;
	person2 = person1;
	cout << "person2:" << person2;
}

为什么operator=返回一个reference to *this ?

为了实现连续赋值,赋值操作符必须返回一个引用指向操作符的左侧实参。这是你为class实现赋值操作符必须遵循的协议。这个协议不仅适用于标准的赋值形式,也适用于+=、-=、*=等等。
   注意,这只是一个协议,并无请执行。如果不遵循它,可能代码一样通过编译。然而这份协议被所有内置类型和标准程序库所提供的类型如string、vector等所遵守。因此除非你有一个标新立异的好理由,不然还是随众吧。
class Person{
	friend ostream& operator<<(ostream& os, const Person& person){
		os << "ID:" << person.mID << " Age:" << person.mAge << endl;
		return os;
	}
public:
	Person(int id, int age){
		this->mID = id;
		this->mAge = age;
	}
	//重载赋值运算符
	Person operator=(const Person& person){
		this->mID = person.mID;
		this->mAge = person.mAge;
		return *this;
	}
	//重载赋值运算符
	Person operator=(int x){
		this->mID = x;
		this->mAge = x;
		return *this;
	}
private:
	int mID;
	int mAge;
};

void test(){
	Person person1(20, 20);
	Person person2(30, 30);
	cout << "person1:" << person1;
	cout << "person2:" << person2;
//由于person2 = person1返回的是临时对象,所以赋值为10并没有改变person2对象
	(person2 = person1) = 10;
	cout << "person2:" << person2;
}
 如果没有重载赋值运算符,编译器会自动创建默认的赋值运算符重载函数。行为类似默认拷贝构造,进行简单值拷贝。


2.视频内容

程序1:

#pragma warning(disable:4996)
//2022年10月12日20:09:55
#include <iostream>
using namespace std;

class Maker
{
public:
    Maker()
    {
        id = 0;
        age = 0;
    }
    Maker(int id, int age)
    {
        this->id = id;
        this->age = age;
    }
public:
    int id;
    int age;
};

void test()
{
    Maker m1(10, 20);
    Maker m2;

    m2 = m1;//赋值操作
    //默认的赋值操作运算符重载函数进行了简单的赋值操作
    cout << m2.id << " " << m2.age << endl;
}

int main()
{
    test();
    system("pause");
    return EXIT_SUCCESS;
}

输出结果:

10 20
请按任意键继续. . .

程序2:

#pragma warning(disable:4996)
//2022年10月12日20:09:55
#include <iostream>
using namespace std;

class Student
{
public:
    Student(const char *name)
    {
        pName = new char[strlen(name) + 1];
        strcpy(pName, name);
    }
    //防止浅拷贝
    Student(const Student &stu)
    {
        pName = new char[strlen(stu.pName) + 1];
        strcpy(pName, stu.pName);
    }
    //重写赋值运算符重载函数
    Student &operator=(const Student &stu)
    {
        //1.不能确定this指向的空间是否能装下stu中的数据,所以先释放this指向的空间
        if( this->pName != NULL )
        {
            delete[] this->pName;
            this->pName = NULL;
        }

        //2.申请堆区空间,大小由stu决定
        this->pName = new char[strlen(stu.pName) + 1];
        //3.拷贝数据
        strcpy(this->pName, stu.pName);

        //4.返回对象本身
        return *this;
    }
    ~Student()
    {
        if( pName != NULL )
        {
            delete[] pName;
            pName = NULL;
        }
    }

    void printStudent()
    {
        cout << "Name:" << pName << endl;
    }
public:
    char *pName;
};

void test02()
{
    Student s1("悟空");
    Student s2("小林");

    s1 = s2;//赋值操作
    s1.printStudent();
    s2.printStudent();

    //复数运算才不会出错
    //s1 = s2 = s3;
}

int main()
{
    test02();
    system("pause");
    return EXIT_SUCCESS;
}

输出结果:

Name:小林
Name:小林
请按任意键继续. . .

程序3:

#pragma warning(disable:4996)
//2022年10月12日20:09:55
#include <iostream>
using namespace std;

class Student
{
public:
    Student(const char *name)
    {
        pName = new char[strlen(name) + 1];
        strcpy(pName, name);
    }
    //防止浅拷贝
    Student(const Student &stu)
    {
        pName = new char[strlen(stu.pName) + 1];
        strcpy(pName, stu.pName);
    }
    //重写赋值运算符重载函数
    Student &operator=(const Student &stu)
    {
        //1.不能确定this指向的空间是否能装下stu中的数据,所以先释放this指向的空间
        if( this->pName != NULL )
        {
            delete[] this->pName;
            this->pName = NULL;
        }

        //2.申请堆区空间,大小由stu决定
        this->pName = new char[strlen(stu.pName) + 1];
        //3.拷贝数据
        strcpy(this->pName, stu.pName);

        //4.返回对象本身
        return *this;
    }
    ~Student()
    {
        if( pName != NULL )
        {
            delete[] pName;
            pName = NULL;
        }
    }

    void printStudent()
    {
        cout << "Name:" << pName << endl;
    }
public:
    char *pName;
};

//赋值运算符重载中为什么要返回引用
void test03()
{
    Student s1("a");
    Student s2("b");
    Student s3("c");

    s1 = s2 = s3;//s3赋值s2,s2赋值给s1

    cout << &(s2 = s3) << endl;
    cout << &s2 << endl;
    //如果返回的是值,s2=s3这个表达式会产生一个新的对象
    //s1=s2=s3,赋值运算符本来的寓意,是s3赋值s2,s2赋值给s1
    //也就是说s2=s3这个表达式要返回s2这个对象
}

int main()
{
    test03();
    system("pause");
    return EXIT_SUCCESS;
}

输出结果:

000000ABA4FBF6F8
000000ABA4FBF6F8
请按任意键继续. . .
posted @ 2022-10-12 21:04  CodeMagicianT  阅读(48)  评论(0编辑  收藏  举报