C++类class:运算符重载

记录一下类运算符重载等相关练习时遇到的细节和坑。

在用类定义一种新数据类型的时候是没有与之相匹配的“+”,“<<”等运算符的,如果要使用的话就要自己对其进行重载,编写相匹配的运算符函数。

如果类内成员开辟了堆内存一定要先写深拷贝函数,并且重载“=”类赋值运算符。

这里以一个数组类为例:

class c_array
{
private:
    int arr_size;
    int* arr;
};

 c_array类的成员分别是arr_size以及arr,分别为数组的元素个数以及数组的存放地址,此时先定义了构造函数,根据输入的维数arr_size在堆上的定义一个数组,由于开辟了堆内存所以定义了一个深拷贝函数。

在定义完各种构造函数和析构函数后,此时进行运算符的重载,如果想实现两个数组相加等于对应元素相加,与数相加等于每个元素等加上这个数,自增同理,相当于自身每个元素+1:

c_array a1;
c_array a2(a1);
c_array b = a1 + a2 + 10000;
++b;

需要定义重载的加号运算符,分别为两个c_array类相加的加法以及c_array与常数相加的加法:

c_array c_array::operator+(c_array& a)
{
    //可以用全局函数重载类运算符(此时不能用this指针,需要两个类参数传入)
    c_array temp(arr_size);
    for (int i = 0; i < arr_size; i++)
    {
        temp.arr[i] = this->arr[i] + a.arr[i];
    }
    return temp;//加法不修改输入值,所以返回值而非引用
}

c_array c_array::operator+(const int& a)
{
    c_array temp(arr_size);
    for (int i = 0; i < arr_size; i++)
    {
        temp.arr[i] = this->arr[i] + a;
    }
    return temp;
}

此时用成员函数来重载两个加法运算符,如果使用全局函数重载,则需要多传入一个c_array类参数并且不能使用this指针。由于加法是值运算,不修改参与加法的变量的原始数据,所以只能使用值传递而非地址传递,使用临时变量记录运算值并返回。

此时利用重载的加法实现c_array类的++自增运算符:

c_array& c_array::operator++()//前置++
{
    *this = *this + 1;
    return *this;
}

c_array c_array::operator++(int)//后置++,占位符区分
{
    c_array temp = *this;
    *this = *this + 1;
    return temp;//修改*this,返回记录的temp的值
}

分别是前置自增和后置自增运算符,后置递增通过参数中的int占位符区分。

我们知道前置递增是先自增再进行外部运算,后置递增是先做外部运算再进行自增,为了实现这一点,前置递增返回自身的引用(直接返回修改后的原始数据*this),后置递增返回临时变量(原始数据*this也进行了修改,但是此时先返回修改之前记录的temp)。

但是在测试时发现此时的自增运算出现bug,在调试过程中自增后的值为乱码,且主程序在运行到最后调用析构函数释放堆内存时崩溃。

根据bug的表现首先怀疑*this = *this + 1;这一行代码,可能是出现了this指针被加法中临时变量地址赋值,由于临时变量在函数运行完就会被释放,所以this指向的地址为空,因此出现了乱码,并且整个程序运行完释放堆空间时试图再次析构野指针this的空间,产生了越界行为致使程序崩溃。

虽然这种情况和bug表现能对上,但是显然代码逻辑并不符合以上想法,自增函数中并没有对this指针进行修改,加法中的临时变量也传递的是temp的值而非地址。为了找到问题所在我打印了加法中的临时变量temp和自增中的*this的地址,结果this和第一个&temp相同,这说明temp确实直接变成了*this,临时变量temp被释放后this变成了野指针

此时加法temp出现了两次,两次分别为主函数中

c_array b = a1 + a2 + 10000;
++b;

这两行代码产生的,而++中的this与第一个temp也就是第一行对c_array b进行赋值时+10000产生的temp地址一致。

联想到程序崩溃的原因,问题的关键在于赋值运算符“=”没有重载,编译器提供的默认赋值运算是将成员的值直接赋值给对象成员,也就是浅拷贝,导致c_array b在定义时其中的*arr成员被直接赋被浅拷贝为了a1+a2+10000中的第二个+号中temp变量的地址。

要解决这个问题必须要手动重载一个深拷贝的类赋值运算:

c_array& c_array::operator=(const c_array& a)
{
    //默认的 类赋值运算符 为浅拷贝实现,如果开辟了堆区内存需要手动重载 深拷贝类赋值,不然会重复析构
    if (this->arr != NULL)
    {
        delete[] this->arr;
        this->arr = NULL;
    }//先把本体清空,再赋新值
    this->arr_size = a.arr_size;
    this->arr = new int[a.arr_size];
    for (int i = 0; i < a.arr_size; i++)
    {
        this->arr[i] = a.arr[i];
    }

    return *this;
}

此时程序可以按照理想情况运行,不会再产生野指针。

  • 15
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值