2021-08-15 C++副本构造器-重载操作符=-图例-调试图解-delete 后 new的理解

本文探讨了C++中对象复制时遇到的问题,特别是当对象包含指针成员时,如何避免因浅拷贝导致的内存问题。通过分析一个示例,展示了如何使用逐位复制引发的指针共享错误,并解释了如何通过重载赋值运算符(深拷贝)来解决这个问题。最后,提供了修复后的代码示例和调试结果。
摘要由CSDN通过智能技术生成

引用案例自:https://blog.csdn.net/crow_n/article/details/51568848

两个指针指向同一个变量时,删除时容易出问题

逐位复制(bitwise copy) :
众所周知,我们可以把 一个对象实例c1赋值给一个类型与之相同的对象c2(例如 MyClass c2=MyClass c1),在赋值过程中编译器将生成必要的代码把"源"对象各属性的值分别赋值给"目标"对象的对应成员,这种赋值行为被称为逐位复制(bitwise copy)。

逐位复制下的普通变量与指针变量 :
逐位赋值在绝大多数场合都是没有问题的,但如果某些成员变量是指针的话,问题就来了:
对象成员进行逐位复制的结果是你将拥有两个一模一样的实例,这两个实例的普通变量虽然名字一样,但是都有不同的地址(&c1和&c2不同)。而这两个副本实例的同名指针(c1.ptrInt和c2.ptrInt)变量却会指向相同的一个地址…

漏洞:
如果两个实例的同名指针变量指向着同一个地址,当删除其中一个对象,它包含的指针也将被删除,但万一此时另一个副本(对象)还在引用这个指针,便会出现问题!

我们都知道两个指针指向同一个变量时如果一个指针被释放那么另一个就会出问题。如图例:
在这里插入图片描述

为了说明问题我做了一个很恶心的小例子

#include <iostream>
#include <string>

class C
{
public :
    C(int v)
    {
        ptrInt=new int;
        *ptrInt=v;
		
        valueInt = v;
    }
	
    ~C()
    {
		
    }
    void DelIntV()
    {
        valueInt=0;
        delete ptrInt;
    }
    
    C(const C& c)
    {
		
    }
    int * ptrInt;
    int valueInt;
private:
    
};


int main()
{
    C c1(2);
    C c2(3);
    c2=c1;
    std::cout<<"c2.ptrInt: "<<c2.ptrInt<<"c2.value "<<*c2.ptrInt<<std::endl;
    std::cout<<"c2.valueInt "<<c2.valueInt<<std::endl;
    c1.DelIntV();
	
    std::cout<<"\n释放c1后:\nc2.ptrInt:"<<c2.ptrInt<<"c2.value: "<<*c2.ptrInt<<std::endl;
    std::cout<<"c2.valueInt: "<<c2.valueInt<<std::endl;
    std::cin.get();
    return 0;
}

在这里插入图片描述调试过程:
在这里插入图片描述
这是把c1赋值给了c2后把指针ptrInt的值输出和valueInt输出,再把c1的指针给delete,valueInt赋值为0,再输出c2的ptrInt和valueInt就会发现指针有问题。

这里记住,此时赋值完以后对象c2里成员指针变量的指向变了!!!

通俗的讲就是创建了c1、c2两个对象,赋值后把c2指到了c1身上。两个对象共用一套器官,c2对c1身体有了控制权,整型变量ValueInt是头发,c1释放以后,也就是c1自杀,器官都死了,c2随同一起死,但是指针释放不影响头发的存在。

为了解决这样的可以重载操作符=

重载操作符的意义在于,把:
(1)c2.ptrInt =c1.ptrInt ;
(2)c2.valueInt = c1.valueInt
两个东西变成:
(1)(*c2.ptrInt )=(*c1.ptrInt), 此处 c2.ptrInt不发生改变,改变的是指针里存放的值;
(2)c2.valueInt = c1.valueInt。

如图例:
在这里插入图片描述

C& operator=(const C &c)
    {
        if(this!=&c)
        {
            delete ptrInt;
            ptrInt = new int;
            *ptrInt= *c.ptrInt;
            valueInt=c.valueInt;
        }
        return *this;
    }

完整代码

#include <iostream>
#include <string>

class C
{
public :
    C(int v)
    {
        ptrInt=new int;
        *ptrInt=v;
		
        valueInt = v;
    }
	
    ~C()
    {		
    }
    void DelIntV()
    {
        valueInt=0;
        delete ptrInt;
    }
    
    C(const C& c)
    {		
    }
    int * ptrInt;
    int valueInt;
	
    C& operator=(const C &c)
    {
        if(this!=&c)
        {
            delete ptrInt;
            ptrInt = new int;
            *ptrInt= *c.ptrInt;
            valueInt=c.valueInt;
        }
        return *this;
    }
private:
    
};


int main()
{
    C c1(2);
    C c2(3);
    c2=c1;
	std::cout<<"c2.ptrInt: "<<c2.ptrInt<<"c2.value "<<*c2.ptrInt<<std::endl;
    std::cout<<"c2.valueInt "<<c2.valueInt<<std::endl;
    c1.DelIntV();
	
    std::cout<<"\n释放c1后:\nc2.ptrInt:"<<c2.ptrInt<<"c2.value: "<<*c2.ptrInt<<std::endl;
    std::cout<<"c2.valueInt: "<<c2.valueInt<<std::endl;
    std::cin.get();
    return 0;
}

在这里插入图片描述重载时调试结果:
在这里插入图片描述释放c1时调试结果
在这里插入图片描述重载的时候构造释放了c2指针指向的内存,然后立马new了一块内存出来,此时c2指针指向的内存位置不变,只是c2指针没有控制权了,new了以后又重新恢复了控制权。

参考以下案例来理解重载操作符里面的两行代码 :
delete ptrInt;
ptrInt = new int;

#include
using namespace std;
void main()
{char*ch1;
ch1=new char(‘h’);
delete ch1;// 只是释放对象的内存空间
*ch1=‘a’;//这里又初始化对象,分配内存
cout<<*ch1<<endl;
}

delete是把堆上面的内存空间释放了,也就是告诉系统,这块内存可以被分配给其他人(因为你既然释放内存了,那系统肯定认为你不需要这块内存了),如果有其他人new后得到这块内存,那肯定会被分配给这个人,但是ch1指针仍然指向这块内存(如果你不做ch1=0的话),也可以对其进行任何操作,如果这块内存已经被分配给别人了,那你这时候通过ch1对其进行的操作可能会导致难以预料的严重后果,所以ch1=0很重要。而对于*ch1=‘a’;需要说明,ch1是放在栈上的,而字符‘a’也是在栈上,只是这时候ch1指向了‘a‘在栈上的地址,所以cout<<*ch1<<endl;肯定是输出a。

char*ch1,*ch2; ch1=new char(‘h’); delete ch1;
delete的时候,系统只是将指针指向的堆空间回收,但是没有将指针变量的值赋值为null,也就是说指针还是指向原来的堆空间,但是这个空间已经失效,所以delete一个指针以后要马上将它赋值为null,不然容易导致野指针的出现。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大大枫

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

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

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

打赏作者

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

抵扣说明:

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

余额充值