在练习过程中做一道题时,遇到了程序崩溃的问题:
源代码如图:(省略了<<、>>重载部分)
class V
{
public:
V()
{
size = 0;
data = new int[10];
}
V operator+(V v1);
V operator-(V v1);
V operator*(V v1);
V& operator=(const V& v1)
{
delete data;
data = NULL;
size = v1.size;
data = new int[size];
for (int i = 0; i < size; i++)
{
this->data[i] = v1.data[i];
}
return *this;
}
V(int size)
{
this->size = size;
this->data = new int[this->size];
}
~V()
{
delete data;
data = NULL;
size = 0;
}
private:
int size;
int* data;
};
V va, vb, vc;
V V::operator+(V v1)
{
va.size = this->size;
va.data = new int[va.size];
for (int i = 0; i < this->size; i++)
{
va.data[i] = v1.data[i] + this->data[i];
}
return va;
}
V V::operator-(V v1)
{
vb.size = this->size;
vb.data = new int[vb.size];
for (int i = 0; i < this->size; i++)
{
vb.data[i] = -v1.data[i] + this->data[i];
}
return vb;
}
V V::operator*(V v1)
{
vc.size = 1;
vc.data = new int[vc.size];
int total = 0;
for (int i = 0; i < this->size; i++)
{
total += v1.data[i] * this->data[i];
}
vc.data[0] = total;
return vc;
}
istream& operator>>(istream& input, V& v1)
{
for (int i = 0; i < v1.size; i++)
{
input >> v1.data[i];
}
return input;
}
int main()
{
int m, n;
while (cin >> m)
{
V v1(m);
cin >> v1;
cin >> n;
V v2(n);
cin >> v2;
if (m != n)
{
cout << "error" << endl;
}
else
{
cout << "X=" << v1;
cout << "Y=" << v2;
V v3 = v1 + v2;
cout << "X+Y=" << v3;
V v4 = v1 - v2;
cout << "X-Y=" << v4;
V v5 = v1 * v2;
cout << "X*Y=" << v5;
}
}
}
调试时发现,如果删去析构函数,则程序正常运行,或者如果自定义拷贝构造函数,程序也可以正常运行,由此可大概知道,应该是没有处理好深浅拷贝的问题。
在这里要明确一点,拷贝构造函数是在初始化的时候调用的,而重载“=”是在赋值的情况下调用的
V v1;
v1=v2;
V v1=v2;
上面两段代码调用的函数是不一样的(第一段调用了默认构造函数和重载的赋值函数,第二段调用了拷贝构造函数)
排查发现,是重载+、-、*时的参数没有用引用,
V v3 = v1 + v2;
cout << "X+Y=" << v3;
V v4 = v1 - v2;
cout << "X-Y=" << v4;
V v5 = v1 * v2;
V V::operator+(V v1);
导致第一行结束后,参数v1复制了主函数对象v1数据成员data的地址,并且由于是局部变量,因此在+函数结束后,主函数对象v1的data已经被释放,当执行第三行时,v1的data又被释放一次,因此引发程序崩溃。
解决办法:
①给V V::operator+(V v1)等函数的参数加上引用
②自定义拷贝构造函数
另外,倘若用方法①修改后,主函数写成这种形式
V v3;
v3 = v1 + v2;
cout << "X+Y=" << v3;
虽然能输出正确结果,但是在程序结束后会发生崩溃,这是因为(v1+v2)的结果储存到了全局变量va中,而v3复制了一份va的数据,因此,在程序结束后会两次释放data,引发崩溃。
总结:最好的解决办法是,凡是遇到自定义了堆区数据成员的情况,为了以防万一,一定要自己定义拷贝构造函数,不要偷懒,另外,养成好习惯自定义析构函数,手动释放堆区的数据。
|