BUG现形记(二)——偷工减料的复制构造函数

最后更新于:2022-04-01 14:38:03

  【课程支撑】[我的 C++程序设计课程教学材料](http://blog.csdn.net/sxhelijian/article/details/7056008)   【摘要】设计数组类,要实现数组类中两个数组相加的运算,程序却陷入死循环。逐层排查,重载的加法正确,重载的赋值运算也看不出问题。跟踪到赋值运算的实现中发现,传递的参数中有异常,终于找出了嫌疑犯——编制的复制构造函数偷工减料。   【阅读提示】现在打开你熟悉的c++,跟随作者的的思路,重走发现嫌犯的过程。   题目是建立专门的数组类处理有关数组的操作,要完成支持数组操作的类的设计,增强C++内置数组类型功能。——见:[第14周-任务1-数组类的构造](http://blog.csdn.net/sxhelijian/article/details/7587387)   有同学向我求助,他的程序如下: ~~~ #include <iostream> using namespace std; class MyArray { private: int *arr; //用于存放动态分配的数组内存首地址 int size; //数组大小 public: MyArray(int sz=50); MyArray(int a[],int sz); //由一个内置类型的数组初始化 MyArray(const MyArray &A); //拷贝构造函数 ~MyArray(void); //析构函数,注意释放空间 MyArray&operator =(const MyArray &A); //重载“=”使得数组对象可以整体赋值 int& operator[](int i); //重载[],使得Array对象也可以如C++普通数组一样,用a[i]形式取出值【选做】 bool operator == (MyArray& A); //重载==,使得Array对象能整体判断两个数组是否相等(size相等且对应元素相等) MyArray operator + (MyArray& A); //重载+,使两个Array对象可以整体相加(前提大小相等)【选做】 friend ostream& operator << (ostream& out,MyArray& A); //重载<<,输出数组 int GetSize(void)const; //取数组大小; void Resize(int sz); //修改数组的大小,如果sz大于数组的原大小,增加的元素初始为;sz小于数组的原大小,舍弃后面的元素【选做】 }; MyArray::MyArray(int sz) { size = sz; arr = new int[size]; for( int i = 0; i < size; i++ ) { *(arr + i) = 0; } } MyArray::MyArray(int a[],int sz) //由一个内置类型的数组初始化 { size = sz; arr = new int[size]; for(int i = 0; i < size; i++) { *(arr + i) = *(a + i); } } MyArray::MyArray(const MyArray &A) //拷贝构造函数 { arr = new int[A.size]; for(int i = 0; i < A.size; i++) { *(arr + i) = *(A.arr + i); } } MyArray::~MyArray(void) //析构函数,注意释放空间 { delete[]arr; } MyArray& MyArray::operator =(const MyArray &A) //重载“=”使得数组对象可以整体赋值 { int n = A.size; if( size != n ) { delete[]arr; arr = new int[n]; size = n; } int* destptr=arr; int* srcptr=A.arr; while(n--) { *destptr=*srcptr; destptr++; srcptr++; } return *this; } int& MyArray::operator[](int i) //重载[],使得Array对象也可以如C++普通数组一样,用a[i]形式取出值【选做】 { return arr[i]; } bool MyArray::operator == (MyArray& A) //重载==,使得Array对象能整体判断两个数组是否相等(size相等且对应元素相等) { bool m; m = true; if( A.size != size ) { m = false; } else { for( int i = 0; i < size; i++ ) if( *(A.arr + i) != *(arr + i) ) { m = false; break; } } return m; } MyArray MyArray::operator + (MyArray& A) { int n=A.size; //取A数组的大小 if (size!=n) //大小不一致不能相加 { cout<<"not same size for add!"<<endl; exit(1); } MyArray a(n); //指定size的数组 for (int i = 0; i < size; i++) { a[i]=arr[i]+A[i]; } return a;//返回当前对象的引用 } ostream& operator << (ostream& out,MyArray& A) //重载<<,输出数组 { for( int i = 0; i < A.size; i++) { out << A[i] << " "; } out << endl; return out; } int MyArray::GetSize(void)const //取数组大小; { return size; } void MyArray::Resize(int sz) //修改数组的大小,如果sz大于数组的原大小,增加的元素初始为;sz小于数组的原大小,舍弃后面的元素【选做】 { int *m = new int(sz); for( int i = 0; i < sz; i++) { *m = 0; } int n = ( sz <= size )?sz:size; for(int j = 0; j < n; j++) { *(m + j) = *(arr + j); } delete[]arr; arr = m; } int main() { int a[10]={1,2,3,4,5,6,7,8,9,10}; int b[10]={4,5,6,7,8,9,10,11,12,13}; MyArray arr1(a,10); MyArray arr2(b,10); MyArray arr3(10); cout<<arr3; arr3 = arr1 +arr2; cout<<arr3; // arr3.Resize(20); // cout<<arr3; // arr3.Resize(5); // cout<<arr3; system("pause"); return 0; } ~~~ 运行程序,结果如下: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-06-07_5756400be11e0.JPG)   光标在第二行一闪一闪,就是不见下文。   此症状一般是陷入了死循环。经阅读程序,已经输出的多个0是160行cout<<arr3;的结果。162行同样的语句应该不会出现问题。焦点锁定在第161行:arr3=arr1+arr2。   arr3=arr1+arr2涉及到两个运算符的重载:+ 和 =。   认真地读一下这两个重载函数,比较明显的是第115行的a[i]=arr[i]+A[i];有些别扭。a和A是MyArray类的对象,而arr是当前对象的一个成员(我想起一条胳膊和一个人要加起来,哪能这样!)修改为a.arr[i]=arr[i]+A.arr[i];(相当于a.arr[i]=this->arr[i]+A.arr[i];这就是胳膊和胳膊加了)。这样,对加法结果正确有了把握。   再次运行,问题依旧。也看不出明显的线索。请来法宝,祭起查找Bug的照妖镜——调试工具来帮忙。   在161行上设置断点运行程序,用F11逐语句执行,进入MyArray::operator +() 函数,即对加法的重载直至结束,未见异常。作为返回值的临时对象a,其中的结果正确。a 的两个属性 size 及 arr 和 arr 为起始地址指向的值,也恰好是该求得值的结果。可以在117行也设置一个断点,观察直至此时 相加结果 a 的值。下面是执行到此处时,从局部变量窗口看到的结果(如果要看a.arr[1]及之后的值,可以用监视窗口): ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-06-07_57564047ef381.JPG)   单步跟踪到MyArray::operator =()函数,即对赋值的重载。很意外地,函数进入到了第59行的 if 语句中。目前程序的运行,所用到的数组,其 size 均为10,应该这个 if 分支是执行不到的。而此时,局部变量的值却让人大吃一惊:MyArray::operator =()中形式参数 A  的 size 成员的值是一个负数!见下图: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-06-07_575640481af11.JPG)   此时该整理一下其中存在的问题了:   (1)问题出在执行arr3=arr1+arr2;上;   (2)计算arr1+arr2,写成函数调用形式是arr1.operator+(arr2),函数调用时,返回的结果是正确的;   (3)接着计算赋值,MyArray::operator =(const MyArray &A) 的形式参数 A 对象,将获得 arr1+arr2 的结果,即实参是  arr1+arr2 的结果,更直观地,执行的是 arr3.operator=(arr1+arr2); ,计算结果本来是正确的,但实参传值给形参后,对象的 size 成员出错了。   在实参给形参传值时,采用的是复制的办法,对类(对象)而言,需要有一个复制构造函数支撑(这个学过)。复制构造函数可以是默认的。但是,如果类中有指针类型的成员时,必须自定义复制构造函数,从而能够处理指针所指向空间的分配和回收问题。   所以,嫌疑犯基本锁定:MyArray类的复制构造函数(也称拷贝构造函数)。看41行开始的拷贝构造函数MyArray::MyArray(const MyArray &A) 的定义,为this->arr成员分配了空间,并将形参对象中指向的数据一一进行复制,惟独没有做的,是为 this->size 赋值!我们需要在这个函数中,增加一行代码:size = A.size;。   至此,案情大白于天下,错误得以纠正,程序得以正确运行。   相关博文:[寻找Bug记实:一个多重继承程序的查错](http://blog.csdn.net/sxhelijian/article/details/7551594)   课程支撑:[我的 C++程序设计课程教学材料](http://blog.csdn.net/sxhelijian/article/details/7056008)
';