c++浅谈类中赋值运算符重载与拷贝构造函数

赋值运算符重载

在c++中,有许多的符号可以来让我们自定义它们的用法,所以可以讨论一下比较特殊的符号“=”号的“自定义”,也就是对它进行重载.

  • 声明
 Baseclass &operator=(const Baseclass &p);

通常运算符重载都是在一个类中定义,这样类与类之间就可以很方便的进行赋值,虽然不使用赋值运算符重载也能实现对类与类之间的赋值,但是当你在delete(free)时就会出现问题。

副本构造器,赋值构造函数与double free问题

我们一般说赋值语句就是拷贝数据而已,将拷贝赋值变量的数据到被赋值变量中,但是如果我们上升到地址的拷贝,比如来进行地址的赋值拷贝,虽然也没问题,因为栈内存无需释放,但是如果我们malloc或new一个堆内存进行地址赋值,这时候就需要注意可能在释放时释放两次相同的地址,所以要使用两个不同的地址就不会发生错误。
所以在处理这类问题,c++中在操作类时我们可以定义副本构造器和赋值构造函数

1.副本构造器
就是将普通的赋值语句包装一下,还是拷贝数据,在初始化时可以用

class example
{
    int Test;
public:
    example(int x):Test(x) //带参数构造函数
    {
        cout << "constructor with argument\n";
    }

    example(const example & ex) //拷贝构造函数
    {
        Test = ex.Test;   //将传入的数据拷贝到类的属性中
        cout << "copy constructor\n";
    }
};

2.赋值(重载)构造函数

为了避免赋值拷贝使用相同的地址,我们通过重载在函数内将地址重新修改

Baseclass &Baseclass::operator=(const Baseclass &rth)
{
    std::cout << "进入等号重载器" << std::endl;
    if(this != &rth)                    //如果两个类不同的话,那么将类内部参数的地址修改为不同于赋值类内部参数的地址
    {
        delete pth;
        pth = (new int);
        *pth = *rth.pth;
    }
    else
    {
        std::cout << "对象相同,不作处理" << std::endl;
    }
    std::cout << "离开等号重载器" << std::endl;
    return *this;
}

这样析构时就避免了double free的问题

  • 总体示例
#include<cctype>
#include<string>
#include<iostream>

class Baseclass
{
    public:
        Baseclass();
        Baseclass(int *p);
        Baseclass(const Baseclass &p);
        ~Baseclass();

        Baseclass &operator=(const Baseclass &p);
        void print();
    private:
        int *pth;
};

Baseclass::Baseclass()
{
    std::cout << "进入Baseclass int 构造器" << std::endl;
    pth = new int;
    std::cout << "离开Baseclass int 构造器" << std::endl;
}

Baseclass::Baseclass(int *p)
{
    std::cout << "进入Baseclass int 构造器" << std::endl;
    pth = p;
    std::cout << "离开Baseclass int 构造器" << std::endl;
}


Baseclass::Baseclass(const Baseclass &p)
{
    std::cout << "进入副本构造器" << std::endl;
    *this = p;
    std::cout << "离开副本构造器" << std::endl;
}

Baseclass::~Baseclass()
{
    std::cout << "进入析构器" << std::endl;
    delete pth;
    std::cout << "离开析构器" << std::endl;
}

Baseclass &Baseclass::operator=(const Baseclass &rth)
{
    std::cout << "进入等号重载器" << std::endl;
    if(this != &rth)
    {
        delete pth;
        pth = (new int);
        *pth = *rth.pth;
    }
    else
    {
        std::cout << "对象相同,不作处理" << std::endl;
    }
    std::cout << "离开等号重载器" << std::endl;
    return *this;
}

void Baseclass::print()
{
    std::cout << pth << std::endl;
}

int main(void)
{
    Baseclass base1(new int(1));
    Baseclass base2(new int(2));
    base1 = base2;

    base1.print();
    base2.print();

    std::cout << "\n" << std::endl;

    Baseclass base3(new int(3));
    Baseclass base4;
    base4 = base3;
    base3.print();
    base4.print();

    std::cout << "\n" << std::endl;

    Baseclass base5(new int(5));
    base5 = base5;
    base5.print();
    return 0;
}

类的赋值初始化与初始化后赋值

这是一个比较绕的话题,在知道了副本构造器和赋值构造器后,我们需要对其执行过程进行一个探索
我们继续来看示例:

class example
{
    int Test;
public:
    example(int x):Test(x) //带参数构造函数
    {
        cout << "constructor\n";
    }

    example(const example & ex) //拷贝构造函数
    {
        Test = ex.Test;
        cout << "copy constructor\n";
    }

    example &operator = (const example &ex)//赋值函数(赋值运算符重载) 返回该类的引用
    {
        cout << "= operator\n";
        Test = ex.Test;
        return *this;                    //解引用,将返回的是exaple类型的类
    }

    void Func(example ex)
    {

    }
};

int main()
{
    example a(2);
    example b(3);
    b = a;
    example c = a;
    b.Func(a);

    return 0;
}

在运行后我们发现其结果是如下:
constructor
constructor
= operator
copy constructor
copy constructor

前两个进入example(int x):Test(x)函数,但是第三个和第四个就有区别了,都是看似赋值但是第四个却进入了副本构造器,所以这就是初始化后赋值与赋值初始化的区别。

无限递归问题

我开始好奇为什么example &operator = (const example &ex)为什么参数需要引用,在查阅资料后发现如果不是引用类型的话会造成无限拷贝赋值递归,如果刚开始来赋值会形成以下死循环
example c = a;->会调用example(const example ex)->const example ex = a->example(const example ex)
即便是初始化后再赋值也是一样,只不过多加了赋值构造函数,所以要使用引用类型!

  • 0
    点赞
  • 0
    评论
  • 0
    收藏
  • 打赏
    打赏
  • 扫一扫,分享海报

©️2022 CSDN 皮肤主题:深蓝海洋 设计师:CSDN官方博客 返回首页

打赏作者

Xavier丶

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值