C++之操作符重载探究(五):赋值运算符重载和拷贝赋值函数(深拷贝)

在这里插入图片描述
前文:C++之操作符重载探究(四):下标运算符重载
涉及到本文所讲知识点的博文:
C++之常引用和浅拷贝探究
C++之一个函数链的简单例子(分文件实现)
C++之Big Three:拷贝构造、拷贝赋值、析构函数探究

赋值运算符重载

浅拷贝产生的问题:

  尽管一个对象可以通过赋值语句赋值给另一个对象,正如我们前面所提到的,这个操作可能只创建一个逻辑拷贝(即成员和成员的浅拷贝)。在浅拷贝中,一个对象的成员,仅仅简单copy另一个对象的成员的值。
  如果对象包含指针成员,拷贝的仅仅是指针变量本身的值(地址),即两个对象的指针成员指向同一块空间。对于指针成员的一对一拷贝可能产生两个问题:

(1)两个指针成员指向同一块空间,改变其中一个,另一个也跟着改变,换句话说,这两个对象互不独立。
(2)两个指针如果共享堆上的空间,我们不知道由谁来负责释放这块内存,要么这块内存没有被释放造成内存泄漏,或者它被释放两次造成二次删除,这都是不允许的。

  因此如果类中有指针成员变量指向堆空间,我们通常需要重载赋值运算符,以实现深拷贝(物理拷贝)。

浅拷贝示例代码:(acc1 = acc;两个对象的title指针指向同一块地方,内容也不独立了)
在这里插入图片描述
分析:
(1) 修改acc1的title后,acc的title也会跟着改变,验证了浅拷贝产生的第一个问题。
(2) 程序在运行后出现崩溃,是由于acc1释放时将acc1.title指向的堆空间删除,而acc再次释放时又试图将acc.title指向的堆空间删除,造成了二次删除,验证了浅拷贝产生的第二个问题。

在这里插入图片描述
附上例代码:

//小问学编程
#include<iostream>
#include<cstring>
using namespace std;

#define MAX_CHAR 10

class Account
{
    
    
    friend ostream& operator<<(ostream& os,const Account& a);
public:
    Account(const char* new_title="Miss",const char* new_owner="unknown",float new_balance=0.0);
    void changeTitle(const char* new_title);
    void changeOwner(const char* new_owner);
    ~Account();
private:
    char* title;
    char owner[MAX_CHAR];
    float balance;
};

Account::Account(const char* new_title,const char* new_owner,float new_balance)
{
    
    
    title=new char[strlen(new_title)+1];
    strcpy(title,new_title);
    strcpy(owner,new_owner);
    balance=new_balance;
}

void Account::changeTitle(const char* new_title)
{
    
    
    if(strlen(new_title)>strlen(title))
    {
    
    
        delete[]title;
        title=new char[strlen(new_title)+1];
        strcpy(title,new_title);
    }
    else
    {
    
    
        strcpy(title,new_title);

    }
}

void Account::changeOwner(const char* new_owner)
{
    
    
    strcpy(owner,new_owner);
}

Account::~Account()
{
    
    
    delete[] title;
    title=NULL;
}

ostream& operator<<(ostream& os,const Account& a)
{
    
    
    os<<a.title<<" "<<a.owner<<" "<<a.balance<<endl;
    return os;
}

int main()
{
    
    
    Account acc("Lou","Mr",100);
    Account acc1;

    cout<<acc<<acc1;
    acc1=acc;//浅拷贝 相当于acc1.title=acc.title。两个指针指向同一块堆内存
    cout<<acc1;

    acc1.changeOwner("jean");
    cout<<acc<<acc1;//两个对象的owner变量,是各自独立的。

    acc1.changeTitle("Dr");
    cout<<acc<<acc1;

    return 0;
}

运行结果:

在这里插入图片描述

解决方案:
  深拷贝,当acc赋值给acc1时(acc1=acc)不能让两个对象的指针成员指向同一块空间,因此我们需要实现等号——赋值运算符重载给acc开辟一块独立的空间来实现深拷贝。

为了实现赋值运算符的重载,我们通常需要做五件事情:

step1: 判断是否是自赋值,避免把一个对象赋值给它自己,如果是对象自己赋值给自己,我们通常不需要做任何事情;
step2: 释放掉指针成员原来指向的内存,避免内存泄漏;
step3: 为指针成员开辟新内存空间;
step4: 拷贝内容,即将右值的内容拷贝给左值;
step5: 返回*this实现链式表达式。
在这里插入图片描述
这个函数也称为拷贝赋值函数(Big Three 之一)

深拷贝示例代码:(vec1 = vec2;通过赋值运算符的重载实现了深拷贝)
在这里插入图片描述
附上例代码:

//小问学编程
#include<iostream>
using namespace std;

class Vector
{
    
    
public:
    Vector(int s,int an_array[]);//构造函数
	const Vector& operator=(const Vector& x);//拷贝赋值函数	
    ~Vector()//析构函数
    {
    
    
        delete[] rep;//两个独立的指针成员,避免了内存的重复删除
    }
    int get_size() const {
    
    return size;}
    int& operator[](int index) {
    
    return rep[index];}
    const int& operator[](int index) const {
    
    return rep[index];}
private:
    int* rep;//指针成员
    int size;
};

Vector::Vector(int s, int an_array[]):rep(new int[s]),size(s)//构造函数
{
    
    
    for (int i= 0;i<size;++i)
    {
    
    
    rep[i] = an_array[i];
    }
}

const Vector& Vector::operator=(const Vector& x)//拷贝赋值函数	
{
    
    
    if(this != &x) //step 1.避免自赋值
    {
    
    
        size = x.size;
        delete[] rep; 	   //step 2.释放成员指针的旧堆内存
        rep = new int[size];  //step 3.为指针成员开辟新内存空间(堆上)
        for (int i = 0; i < size; ++i)
        {
    
    
            rep[i] = x.rep[i];//step 4.向新内存拷贝内容
        }
    }
    return *this; //step 5.返回*this,即返回新对象本身,即新对象的引用。
}

ostream& operator<<(ostream& os, const Vector& x)//output全局函数
{
    
    
    int s = x.get_size( );
    for (int i = 0; i < s; ++i)
    {
    
    
        os<< x[i]<<" ";
    }
    return os;
}

int main()
{
    
    
    int array1[5] = {
    
    1,2,3,4,5};
    int array2[10] = {
    
    6,7,8,9,10,11,12,13,14,15};
    Vector vec1( 5, array1);
    Vector vec2( 10, array2);
    cout<<vec1<<vec2<<endl;
    vec1 = vec2;//深拷贝的赋值
    cout<<vec1<<vec2<<endl;//此时,两个数组内容虽然一样,但内存中的位置不同。
    vec2[8] = 100;
    cout<<vec1<<vec2<<endl;//对vec2的修改完全不影响vec1的内容。
    return 0;
}

运行结果:

本节未完,待更新
C++之操作符重载探究(六):重载函数调用符( )

猜你喜欢

转载自blog.csdn.net/weixin_43297891/article/details/111396128