【问题标题】:Is this safe to deallocate memory from a pointer of a different type than when it was allocated?从与分配时不同类型的指针释放内存是否安全?
【发布时间】:2012-12-17 06:13:06
【问题描述】:

我有一个关于在 C++ 中释放内存的问题:

typedef struct type1
{
   int a;
   int b;
   float c;
} Type1;

typedef struct type2
{
   int a;
   int b;
} Type2;

void *p = new Type1;

delete (Type2 *)p;

在这种情况下,p 指向的内存区域在p 被转换为不同大小的类型后是否会被完全删除?

【问题讨论】:

  • @Johnsyweb base 没有虚拟析构函数,所以这仍然是未定义的行为
  • 为什么不引入一个带有virtual dtor struct base { int a; int b; virtual ~base() { } }; struct Type1: public base { float c; }; struct Type2: public base { }; base* p = new Type1; delete p; 的通用基类?
  • @Johnsyweb:如果您想使用继承,如果 Type1(由属性 c 扩展)派生自 Type2 会更容易。就像 Praetorian 已经指出的那样,Type2(基本类型)应该有虚拟析构函数。
  • @Liho:这可能更容易,但给出的示例通常与现实世界的案例相比大大减少。我现在已经包含了virtual ~base() { }

标签: c++ memory dynamic


【解决方案1】:

行为未定义。在这种情况下,动态分配的对象只能通过Type1类型的指针来删除。

首先,在delete表达式中使用(Type2 *)p获得的指针,违反了别名规则。 p 指向的对象可以通过一组有限的类型使用。 C++03 的规则可以在in an answer to another question 找到。 C++11 规则类似(差异与您问题的答案无关)。

即使程序没有违反严格的别名规则,它也会违反delete 表达式的要求。规范声明(C++11 §5.3.5[expr.delete]/3):

如果待删除对象的静态类型与其动态类型不同,则静态类型应为待删除对象的动态类型的基类,并且 静态类型应具有虚拟析构函数,否则行为未定义。

在您的delete 表达式中,对象的静态类型是Type2,而动态类型是Type1。类型不同,但静态类型不是动态类型的基类。

【讨论】:

  • 您是否必须取消引用类型双关指针才能违反严格的别名规则?
  • 但是在 C 中会发生什么?如果 p = (Type1 *)malloc(sizeof(Type1));然后 free((Type2 *)p)?
  • @ratzip free 接受 void * 参数,因此演员表无关紧要。
  • @ratzip: free 只释放内存; delete 还需要在释放内存之前调用适当的析构函数。
【解决方案2】:

这将是一个非常糟糕的主意,因为您会要求编译器安排在 Type1 指针上运行 Type2::~Type2,而该析构函数可能会引用对象的末尾。

在传统环境中,最终释放内存是可以的,因为operator delete 调用free,它不关心您在编译时调用它的类型。但是,在不那么传统的环境中,这可能是一场灾难。

【讨论】:

    【解决方案3】:

    虽然James已经完美回答了这个问题,但我想指出一件事:

    在正确的 C++ 代码中,您几乎永远不会使用 void 指针,这意味着您的代码可能看起来像这样:

    SubType *p = new SubType;
    BaseType* pB = (BaseType*)p;
    delete pB;
    

    在这种情况下,即使BaseType 有适当的虚拟构造函数,如果SubType 不是从BaseType 派生的,仍然可能存在未定义的行为。普通的 C 风格转换在这里不是很幸运的选择。

    但如果您使用dynamic_cast,编译器很可能不会让您这样做,以防p 不指向多态类型的对象。即使p 指向多态类型的对象,但BaseType 不会是SubType 的基类型,dynamic_cast 也会返回NULL,您可以适当地处理这种状态:

    SubType *p = new SubType;
    BaseType* safePtr = dynamic_cast<BaseType *>(p);
    if (!safePtr) // p doesn't point to object of type derived from BaseType
        ...       // handle this situation
    else          // p points to object of polymorphic type derived from BaseType
        delete safePtr;
    

    【讨论】:

      猜你喜欢
      • 2011-11-15
      • 1970-01-01
      • 2015-09-29
      • 1970-01-01
      • 2017-05-07
      • 2023-04-09
      • 1970-01-01
      • 1970-01-01
      • 2020-11-22
      相关资源
      最近更新 更多