1

这可能是不可能的,但我想我会问...

有没有人可以想到在不修改变量本身的类的情况下跟踪自动变量是否已被删除?例如,考虑以下代码:

const char* pStringBuffer;
{
    std::string sString( "foo" );
    pStringBuffer = sString.c_str();
}

显然,在块之后,pStringBuffer 是一个悬空指针,它可能有效也可能无效。我想要一种方法来拥有一个包含 pStringBuffer 的包装类(带有一个用于 const char* 的强制转换运算符),但断言它引用的变量仍然有效。通过更改引用变量的类型,我当然可以做到(例如,提升 shared_ptr/weak_ptr),但我希望能够在不对引用类型施加限制的情况下做到这一点。

一些想法:

  • 我可能需要更改赋值语法以包含引用的变量(这很好)
  • 我也许可以查看堆栈指针来检测我的包装类是否比引用的类“晚”分配,但这似乎很老套且不标准(C++ 没有定义堆栈行为)。不过,它可以工作。

想法/出色的解决方案?

4

4 回答 4

1

一般来说,在 C++ 中根本不可能,因为指针太“原始”。此外,查看您是否在引用的类之后分配是行不通的,因为如果您更改字符串,则 c_str 指针可能会更改。

在这种特殊情况下,您可以检查字符串是否仍然为 c_str 返回相同的值。如果是,您可能仍然有效,如果不是,那么您的指针无效。

作为调试工具,我建议使用高级内存跟踪系统,例如 valgrind(恐怕仅适用于 linux。类似的程序适用于 windows,但我相信它们都需要花钱。这个程序是我安装 linux 的唯一原因在我的 Mac 上)。以你的程序执行速度慢得多为代价,valgrind 检测你是否曾经从一个无效的指针中读取。虽然它并不完美,但我发现它可以检测到许多错误,尤其是这种类型的错误。

于 2008-10-16T23:10:21.840 回答
0

您可能会发现一种有用的技术是用您自己的实现替换new/delete运算符,这些实现将使用的内存页面(由您分配的)标记operator new为在释放(由您的释放operator delete)时不可访问。您将需要确保内存页面永远不会被重复使用,但是由于内存耗尽,运行时长度会受到限制。

如果您的应用程序在释放内存页面后访问它们,如上面的示例所示,操作系统将捕获尝试的访问并引发错误。它本身并不完全跟踪,因为应用程序将立即停止,但它确实提供反馈:-)

此技术适用于狭窄的场景,不会捕获所有类型的内存滥用,但它可能很有用。希望有帮助。

于 2008-10-16T23:08:08.287 回答
0

您可以制作一个在您提到的简单情况下工作的包装类。也许是这样的:

X<const char*> pStringBuffer;
{
    std::string sString( "foo" );
    Trick trick(pStringBuffer).set(sString.c_str());
} // trick goes out of scope; its destructor marks pStringBuffer as invalid

但它无助于更复杂的情况:

X<const char*> pStringBuffer;
{
    std::string sString( "foo" );
    {
        Trick trick(pStringBuffer).set(sString.c_str());
    } // trick goes out of scope; its destructor marks pStringBuffer as invalid
}

在这里,失效发生得太快了。

大多数情况下,您应该只编写尽可能安全的代码(请参阅:智能指针),但不安全(请参阅:性能、低级接口),并使用工具(valgrind、Purify)来确保不会漏掉任何东西。

于 2008-10-16T23:57:28.687 回答
0

鉴于“pStringBuffer”是 sString 超出范围后示例中唯一存在的部分,您需要对其进行一些更改或替代,以反映这一点。一个简单的机制是一种作用域保护,作用域匹配 sString,它在 pStringBuffer 被销毁时影响它。例如,它可以将 pStringBuffer 设置为 NULL。

要在不改变“变量”类的情况下做到这一点,只能通过多种方式完成:

  • 在与 sString 相同的范围内引入一个不同的变量(为了减少冗长,您可以考虑使用宏来同时生成这两个东西)。不太好。

  • 用模板 ala X sString 包装:这是否是“修改变量的类型”是有争议的……另一种观点是 sString 成为同一变量的包装器。它还受到影响,您能做的最好的事情就是让模板构造函数将参数传递给包装的构造函数,最多可以达到一些有限的 N 个参数。

这些都没有多大帮助,因为它们依赖于开发人员记住使用它们。

更好的方法是使“const char* pStringBuffer”简单地成为“std::string some_meaningful_name”,并根据需要分配给它。考虑到引用计数,99.99% 的时间都不会太贵。

于 2008-10-17T10:06:20.860 回答