请注意,您的代码
void EmployeeRecord::destroyCustomerList()
{
if(m_oCustomerList != NULL)
{
delete m_oCustomerList;
m_oCustomerList = NULL;
}
}
简化为:
void EmployeeRecord::destroyCustomerList()
{
delete m_oCustomerList;
m_oCustomerList = NULL;
}
delete
在 C++ 中对空指针调用运算符是安全的。它什么也不做。换句话说,对 null 的检查已经“内置”了。
一旦你删除了一个对象,它就不再存在了,指向那个对象的指针变成了不确定的值(所以把那个指针的所有副本都清空并不是一个坏主意)。
在实际的 C++ 实现中,内存真正发生的事情,而不是抽象意义上的,是它继续存在于同一地址,但被标记为空闲,以便可以为其他目的分配它。来自程序(可能是完全不相关的模块)或可能来自系统中的另一个程序的分配请求可以获得该内存以供自己使用。
指向不再存在的对象的指针的任何使用都是“未定义的行为”。确实存在用于安全验证此类指针的函数,但它们非常特定于平台并且很少完美。
问题是,虽然一个实现确认一个指针是坏的并不是特别困难,但不可能确认一个指针是好的。我们可以遍历内存分配器的内部内存数据结构,以确定某个指针指向空闲存储。但是如果存储随后被分配怎么办?然后指针不再指向空闲存储。但它也不是指分配的原始对象!这被称为“ABA 歧义”:因为一些 A 变成了 B,然后又变成了 A,与原来的 A 没有区别。
存在解决 ABA 歧义的方法(如果不是完全,至少是部分)。例如,指针变得“胖”,因此它们除了地址位之外还有一个额外的字段。该字段可以包含一个序列号,用于标记从分配器返回的指针。现在,当一个对象被删除并重新分配时,指向同一位置的新指针具有不同的序列号:我们有 ABA'。指针 A 坏了,变成了 B,但是当它复活时,它又变成了 A'。如果我们要求系统验证 A,它会正确地确定 A 是坏的,因为它没有预期的序列号。指向对象的正确有效指针是 A',它与 A 不匹配。
但是,序列号字段只有这么多位宽,它们最终会回绕。所以ABA问题并没有真正解决。好与坏指针的验证只会变得更加可靠。为了彻底解决 ABA 问题,系统必须始终分发不等于任何仍可使用的指针的新指针。这意味着永远不要真正释放任何东西(从而耗尽内存)或实施垃圾收集。(这意味着delete
实际上什么都不做:被删除的对象被销毁,但会一直留在内存中,直到它们被垃圾回收,当程序不再记住指针的任何副本时会发生这种情况。此时,程序不再记得 A,并且所以可以重新引入A,不存在ABA问题。)
要使所有指针“变胖”,您必须更改整个工具链和运行时:编译器、库等。由于大型程序往往具有多个内存分配器,因此存在进一步的困难。如果你问错误的分配器“这个指针是否有效”,它只能说“这个指针不是来自我的竞技场”。您可以做的另一种方法是发明自己的指针并将它们实现为 C++ 中的智能指针。您的指针可以支持一种is_valid
尽可能可靠的方法(以某种方式处理 ABA 问题:部分使用一些序列号等,或者通过实施您自己的垃圾收集方案。)