C++中动态内存分配引发问题的解决方案

首先,大家要知道, C ++类有以下这些极为重要的函数:

  一:复制构造函数。

  二:赋值函数。

  我们先来讲复制构造函数。什么是复制构造函数呢?比如,我们可以写下这样的代码: Wrong test1(test2); 这是进行初始化。我们知道,初始化对象要用构造函数。可这儿呢?按理说,应该有声明为这样的构造函数: Wrong(const Wrong &); 可是,我们并没有定义这个构造函数呀?答案是, C ++提供了默认的复制构造函数,问题也就出在这儿。

  ( 1 ):什么时候会调用复制构造函数呢?(以 Wrong 类为例。)

  在我们提供这样的代码: Wrong test1(test2) 时,它会被调用;当函数的参数列表为按值传递,也就是没有用引用和指针作为类型时,如: void show_wrong(const Wrong) ,它会被调用。其实,还有一些情况,但在这儿就不列举了。

  ( 2 ):它是什么样的函数。

  它的作用就是把两个类进行复制。拿 Wrong 类为例, C ++提供的默认复制构造函数是这样的:
Wrong(const Wrong& a){str=a.str;len=a.len;}

  在平时,这样并不会有任何的问题出现,但我们用了 new 操作符,涉及到了动态内存分配,我们就不得不谈谈浅复制和深复制了。以上的函数就是实行的浅复制,它只是复制了指针,而并没有复制指针指向的数据,可谓一点儿用也没有。打个比方吧!就像一个朋友让你把一个程序通过网络发给他,而你大大咧咧地把快捷方式发给了他,有什么用处呢?我们来具体谈谈:

  假如, A 对象中存储了这样的字符串:“ C ++”。它的地址为 2000 。现在,我们把 A 对象赋给 B 对象: Wrong B=A 。现在, A B 对象的 str 指针均指向 2000 地址。看似可以使用,但如果 B 对象的析构函数被调用时,则地址 2000 处的字符串“ C ++”已经被从内存中抹去,而 A 对象仍然指向地址 2000 。这时,如果我们写下这样的代码: cout << A << endl; 或是等待程序结束, A 对象的析构函数被调用时, A 对象的数据能否显示出来呢?只会是乱码。而且,程序还会这样做:连续对地址 2000 处使用两次 delete 操作符,这样的后果是十分严重的!

  本例中,有这样的代码:
Wrong* wrong1=new Wrong(test1);cout << *wrong1 << endl;delete wrong1;

  假设 test1 str 指向的地址为 2000, wrong str 指针同样指向地址 2000 ,我们删除了 2000 处的数据,而 test1 对象呢?已经被破坏了。大家从运行结果上可以看到,我们使用 cout << test1 时,一点反应也没有。而在 test1 的析构函数被调用时,显示是这样:“这个字符串将被删除:”。

  再看看这段代码:
cout << " 使用错误的函数: " << endl;show_wrong(test2);cout << test2 << endl;// 这一段代码出现严重的错误!

   show_wrong 函数的参数列表 void show_wrong(const Wrong a) 是按值传递的,所以,我们相当于执行了这样的代码: Wrong a=test2; 函数执行完毕,由于生存周期的缘故,对象 a 被析构函数删除,我们马上就可以看到错误的显示结果了:这个字符串将被删除: ?= 。当然, test2 也被破坏了。解决的办法很简单,当然是手工定义一个复制构造函数喽!人力可以胜天!
Wrong::Wrong(const Wrong& a){len=a.len;str=new char(len+1);strcpy(str,a.str);}

  我们执行的是深复制。这个函数的功能是这样的:假设对象 A 中的 str 指针指向地址 2000 ,内容为“ I am a C++ Boy! ”。我们执行代码 Wrong B=A 时,我们先开辟出一块内存,假设为 3000 。我们用 strcpy 函数将地址 2000 的内容拷贝到地址 3000 中,再将对象 B str 指针指向地址 3000 。这样,就互不干扰了。

  大家把这个函数加入程序中,问题就解决了大半,但还没有完全解决,问题在赋值函数上。我们的程序中有这样的段代码:
Wrong wrong3;wrong3=test4;

  经过我前面的讲解,大家应该也会对这段代码进行寻根摸底:凭什么可以这样做: wrong3=test4 ???原因是, C ++为了用户的方便,提供的这样的一个操作符重载函数: operator= 。所以,我们可以这样做。大家应该猜得到,它同样是执行了浅复制,出了同样的毛病。比如,执行了这段代码后,析构函数开始大展神威 ^_^ 。由于这些变量是后进先出的,所以最后的 wrong3 变量先被删除:这个字符串将被删除:第四个范例。很正常。最后,删除到 test4 的时候,问题来了:这个字符串将被删除: ?= 。原因我不用赘述了,只是这个赋值函数怎么写,还有一点儿学问呢!大家请看:

  平时,我们可以写这样的代码: x=y=z 。(均为整型变量。)而在类对象中,我们同样要这样,因为这很方便。而对象 A=B=C 就是 A.operator=(B.operator=(c)) 。而这个 operator= 函数的参数列表应该是: const Wrong& a ,所以,大家不难推出,要实现这样的功能,返回值也要是 Wrong& ,这样才能实现 A B C 。我们先来写写看:
Wrong& Wrong::operator=(const Wrong& a){delete [] str;// 先删除自身的数据 len=a.len;str=new char[len+1];strcpy(str,a.str);// 此三行为进行拷贝 return *this;// 返回自身的引用 }

  是不是这样就行了呢?我们假如写出了这种代码: A=A ,那么大家看看,岂不是把 A 对象的数据给删除了吗?这样可谓引发一系列的错误。所以,我们还要检查是否为自身赋值。只比较两对象的数据是不行了,因为两个对象的数据很有可能相同。我们应该比较地址。以下是完好的赋值函数:
Wrong& Wrong::operator=(const Wrong& a){if(this==&a)return *this;delete [] str;len=a.len;str=new char[len+1];strcpy(str,a.str);return *this;}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值