调试 C++ 公共成员变量的修改/访问






3.66/5 (10投票s)
2006年11月20日
3分钟阅读

27234
使用运算符重载来调试公共成员变量的修改/访问的一个技巧
引言
在许多 C++ 维护/增强项目中,public
成员变量是一个现实,特别是当这些项目从 C 转换为 C++ 时。在这些项目中,我在调试时不断遇到的一个问题是如何设置断点,以便找出某个 public
成员变量何时被修改或被访问。这些成员变量通常是基本的 C、C++ 类型,如 int
、double
等。导致这种情况的常见原因是 C 结构体被提升为 C++ 类,而没有任何访问器或修改器方法。这通常发生在 C 到 C++ 的迁移过程中,为了加快项目速度,或者有时是因为一些懒惰的同事使用了 public
成员变量作为捷径。
为什么如此令人沮丧?
令人沮丧的是,看到相同的成员变量名称被用在 10 个不同的类中,而这 10 个不同的类又被用在 100 个不同的文件中。当你搜索该变量时,你会得到 1000 个结果,并且一直想知道这些结果中哪一个是我的类实例被修改的正确位置。如果你经常遇到这种麻烦,那算你幸运。
调试时有哪些选择?
在 Visual Studio 调试器中,有一些可用的解决方案可以解决这个问题。最受欢迎的一种是在成员变量被修改时设置断点。这个解决方案在大多数情况下都运行良好。但是有一个限制 - 必须在运行时为类实例上下文中的修改设置断点,并且你必须很好地了解执行流程才能获得正确的实例。当你有很多类实例时,你不知道哪个特定的类才是你真正感兴趣的。在正确的上下文中设置断点之前,你将不得不付出很多努力,或者在最坏的情况下,你不知道执行流程,并且无法找到你感兴趣的任何上下文。
无论如何,没有办法设置断点来检查成员变量何时被访问。
“问题代码”
让我们看看代码对于我所说的调试问题是什么样的...
class RequestA
{
public:
//Other member variables
int v1;
int v2;
//member variable of our interest
int status;
};
class RequestB
{
public:
//Other member variables
int v3;
int v4;
//member variable of our interest
int status;
};
void UserFunction1(int condition)
{
RequestA reqA1;
RequestA reqA2;
RequestB reqB1;
RequestB reqB2;
reqA1.status = 0;
reqA2.status = reqA1.status;
reqB1.status = reqA1.status;
if (reqB1.status == 0)
{
reqB2.status = reqB1.status;
}
switch(condition)
{
case 10:
//Some code
//
//
//
reqA2.status = 0;
//some other processing
//
reqB1.status = 0;
//Some code
//
//
//
reqA1.status = 2;
case 11:
reqA1.status = 3;
//Some code
//
//
//
reqA2.status = 0;
//Some code
//
//
//
reqB1.status = 0;
case 16:
reqA1.status = 3;
//Some code
//
//
//
reqB2.status = 4;
//Some code
//
//
//
case 19:
reqA1.status = 5;
//Some code
//
//
//
reqA1.status = 3;
reqB1.status = 3;
}
}
void UserFunction2(int anotherCondition)
{
RequestA reqA1;
RequestA reqA2;
RequestB reqB1;
RequestB reqB2;
reqA1.status = 1;
reqA2.status = 2;
reqB1.status = 3;
reqB2.status = 4;
//Some complex and long algorithm modifying status variable
//...
//...
reqA1.status = reqA2.status;
reqA2.status = reqB1.status ;
reqB1.status = reqB2.status ;
reqB2.status = reqA2.status;
}
我为修改或访问设置断点的解决方案
在这种情况下,我发现以下简单的 C++ 技巧非常有用,可以捕获修改 public
变量的代码。我围绕基本的 int
定义一个包装类,我们称之为 BasicIntWrapper
。它提供了提取 int
值并将其分配回去所需的运算符。
class BasicIntWrapper
{
int m_i;
public:
operator int()
{
return m_i;
}
int operator = (const int& value)
{
return (m_i = value);
}
//Some more additional operators as needed..
//...
//...
//...
};
现在,对于调试版本,让我们将类成员状态细化为 BasicIntWrapper
status; 让我们看看重新定义的类 RequestB
。
class RequestB
{
public:
//Other member variables
int v3;
int v4;
//member variable of our interest
#ifdef _DEBUG
BasicIntWrapper status;
#else
int status;
#endif
};
一个更好的方法是将此包装类定义为 RequestB
的成员类。这在两个方面有所帮助
- 捕获每个类的修改
- 处理名称冲突
class RequestB
{
public:
//Other member variables
int v3;
int v4;
//member variable of our interest
#ifdef _DEBUG
class BasicIntWrapper
{
int m_i;
public:
operator int()
{
return m_i;
}
int operator = (const int& value)
{
return (m_i = value);
}
//Some more additional operators as needed..
//...
//...
//...
};
BasicIntWrapper status;
#else
int status;
#endif
};
现在,如果在类 BasicIntWrapper
的运算符中设置断点,您将捕获所有类范围的修改。
最终评论
我的经验是 - 对于这个问题没有银弹,但是这个技巧有助于在很大程度上改进断点的位置,然后再在 Visual Studio 中使用断点。
作者:Vishal Jadhav
电子邮件:vishalya@yahoo.com