您的位置:首页 > 其它

一个有趣的Bug修正记

2010-07-22 21:15 357 查看
最近无聊写了一个Managed类用来托管指针(C++中)。代码如下:(不存在内存泄露)

namespace Logic
{
template <class T> class Managed
{
T *datum; int *refCnt;
public:
//Constructors
Managed() : datum(0), refCnt(0) { }
Managed(const Managed<T> &src)
: datum(src.datum), refCnt(src.refCnt)
{
if (refCnt) ++*refCnt;
}
Managed(T *src) : datum(0), refCnt(0)
{
if (src)
{
datum = src;
refCnt = new int(1);
}
}
//Destructor
~Managed()
{
if (refCnt && !--*refCnt)
{
delete datum;
delete refCnt;
}
}
//Cast Operators
const T &operator * () const { return *datum; }
T &operator * () { return *datum; }
const T *operator -> () const { return datum; }
T *operator -> () { return datum; }
operator bool() { return datum; }
//Assignment Operators
const Managed<T> &operator = (const Managed<T> &src)
{
this->~Managed();
datum = src.datum;
refCnt = src.refCnt;
if (refCnt) ++*refCnt;
return *this;
}
const Managed<T> &operator = (T *src)
{
this->~Managed();
if (src) { datum = src; refCnt = new int(1); }
else { datum = 0; refCnt = 0; }
return *this;
}
//Comparement Operators
bool operator == (const Managed<T> &rval) { return datum == rval.datum; }
bool operator == (T *rval) { return datum == rval; }
bool operator != (const Managed<T> &rval) { return !operator == (rval); }
bool operator != (T *rval) { return !operator == (rval); }
};
}


这个类有个很严重的问题,就是使用链表的时候。比如有如下的链表定义:

typedef struct node_
{
int datum;
Managed<struct node_> next;
node_() { datum = 0; next = 0; }
} node;


这是一个很一般的链表节点结构。下面,我们使用这个结构:

int main()
{
int n;
cin >> n;
Managed<node> head, p;
head = new node();
p = head;
for (int i = 0; i < n; ++i)
{
p = p->next = new node();
cin >> p->datum;
}
p = head->next;
head = NULL;
while (p)
{
cout << p->datum << ' ';
p = p->next;
}
system("pause");
return 0;
}


这个看起来是没什么问题,但是运行的时候有Access Violation。前后加入调试语句之后,发现问题出现在:

p = p->next; (在输出那段里面)

这个有什么问题呢?好像是对象已经被回收了。神奇啊!看看Managed的代码。

在“=运算符”中,有个很大的问题:

const Managed<T> &operator = (const Managed<T> &src)
{
this->~Managed();
datum = src.datum;
refCnt = src.refCnt;
if (refCnt) ++*refCnt;
return *this;
}
const Managed<T> &operator = (T *src)
{
this->~Managed();
if (src) { datum = src; refCnt = new int(1); }
else { datum = 0; refCnt = 0; }
return *this;
}


问题出现在“this->~Managed();”这里。经过一番思考和画图后,发现p = p->next;的执行方式如下:

p.operator = (p.operator->()->next);

调用operator->没问题。

调用operator=:

调用析构函数

析构对象(p的当前对象)

之前,head=NULL,已经回收了head(head这里是一个无效数据),所以p的引用计数为0,回收p托管的对象。

回收p托管的对象之后,p托管的对象的next的引用计数也变成0,顺便也回收了,就这样,把后面的节点都回收了……

析构完毕

p托管了不可用内存(有的人称为“野指针”“悬空指针”“悬垂指针”)。

结束调用operator=。

然后下一轮循环的时候,就出现了Access Violation。

于是,代码改为使用备份:

const Managed<T> &operator = (const Managed<T> &src)
{
T *backupDatum = datum;
int *backupRefCnt = refCnt;
// 备份对象和引用计数
datum = src.datum;
refCnt = src.refCnt;
if (refCnt) ++*refCnt;
if (backupRefCnt && !--*backupRefCnt)
{
delete backupDatum;
delete backupRefCnt;
}
return *this;
}
const Managed<T> &operator = (T *src)
{
T *backupDatum = datum; int *backupRefCnt = refCnt;
if (src) { datum = src; refCnt = new int(1); }
else { datum = 0; refCnt = 0; }
if (backupRefCnt && !--*backupRefCnt)
{
delete backupDatum;
delete backupRefCnt;
}
return *this;
}


至此,问题解决。希望本文能给广大Developer一些Debug的帮助!

by Logic 版权所有,未经许可,不得转载
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: