今天写一个小题:设11和12均为顺序表,编写一个函数,找出并输出他们的 最大子前级串,并比较剩余串的大小。在将对象作为参数传递给compare函数时,使用传引用的方式传递对象参数,编译可以通过,然而当我使用传值方式传递对象参数,编译器报错:
double free or corruption (fasttop)
。
以下是程序代码
const int defaultSize = 10;
class SeqList {
protected:
int* data;
int maxSize; //表最大可容纳项数
int last; //当前表大小
public:
SeqList(int sz = defaultSize);
~SeqList();
int Length() const; //计算表长度
int getData(int i) const; //取第i歌表项的值,存放在x中
void setData(int i, int x);
bool Insert(int i, int x); //插入x在第i歌表项之后
void Remove(int i);
void output(); // 输出顺序表
};
SeqList::SeqList(int sz)
{
if (sz > 0) {
maxSize = sz;
last = -1;
data = new int[maxSize];
if (data == NULL)
exit(1);
}
}
SeqList::~SeqList()
{
delete[]data;
}
int SeqList::Length() const
{
return last + 1;
}
int SeqList::getData(int i) const
{
return data[i];
}
void SeqList::setData(int i, int x)
{
data[i] = x;
}
bool SeqList::Insert(int i, int x)
{
if (last == maxSize - 1)
return false;
if (i<0 || i>last + 1)
return false;
int j;
for (j = last; j >= i; j--)
data[j + 1] = data[j];
data[i] = x;
last++;
return true;
}
void SeqList::Remove(int i)
{
int j;
for (j = i; j <= last - 1; j++)
data[j] = data[j + 1];
last--;
}
void SeqList::output()
{
int i;
for (i = 0; i <= last; i++)
cout << data[i] << " ";
cout << endl;
}
void compare(SeqList a, int n, SeqList b, int m)
{
int i = 0;
while (a.getData(i) == b.getData(i))
{
i++;
if (i >= m || i >= n)
break;
}
for (int j = 0; j < i; j++)
{
cout << a.getData(j) << ' ';
}
cout << endl;
if (i == m && i == n)
cout << '=' << endl;
else if (i >= m || a.getData(i) > b.getData(i))
cout << '>' << endl;
else if (i >= n || a.getData(i) < b.getData(i))
cout << '<' << endl;
}
compare(l1, n, l2, m);
通过百度发现,double free or corruption (fasttop),是由二次delete同一个指针导致的。
当
使用传值方式传递对象参数时,会创建一个新的临时对象,创建该对象时会利用拷贝构造函数通过复制已有对象的值来创建。而如果我们没有显示的定义拷贝构造函数,c++将会提供一个缺省的拷贝构造函数(copy constructor)。而缺省的拷贝构造函数进行对象赋值时,采用的是“浅拷贝”(shallow copy),而不是“深拷贝”(deep copy)实现。即如果类的成员中存在指针,浅拷贝只会复制指针保存的地址值,而不是复制指针指向的内容。
对于本程序而言,即compare函数中创建的SeqList对象a,其指针成员 a.data ,与compare(l1, n, l2, m) 函数调用中传入的 l1的指针成员 l1.data 地址相同,在函数中对a.data的操作即对l1.data进行操作,因为在构建对象a时,浅拷贝仅实现了a.data = l1.data,其复制的是data指针保存的地址值,而不是data指针指向的内容。
通过缺省拷贝构造函数创建的对象的数据域中存在指针,就会出现二次 delete 指针的错误。就该程序而言,在拷贝构造时,临时创建的对象在赋值完毕后,会调用析取函数,delete一次data数组,而由于是“浅拷贝”,所以在跳出compare函数,再次调用析取函数时,就会对data数组进行二次删除,程序出错。
要解决这个问题,需要我们自定义拷贝构造函数,实现“深拷贝”。
自定义的拷贝构造函数如下
SeqList::SeqList (SeqList& l)
{
maxSize = l.maxSize;
last = l.last;
data = new int[l.maxSize];
for (int i = 0; i <= l.last; i++)
{
data[i] = l.data[i];
}
}
运行程序,可以顺利执行。
通过此次问题可以看出,虽然传值方式和传引用方式都是允许的,但在行得通的情况下,将对象通过引用传递给函数作参数更加有效,这样不但不需要额外的时间和内存空间,而且会更加直观。