【问题标题】:Mixing operator and expression new/delete混合运算符和表达式 new/delete
【发布时间】:2023-03-23 08:35:02
【问题描述】:

考虑以下代码:

unsigned char* a = new unsigned char[100];
void* a1 = reinterpret_cast<void*>(a);
operator delete[](a1);

short* b = new short[100];
void* b1 = reinterpret_cast<void*>(b);
operator delete[](b1);

在我看来,它是标量类型的有效语法,因为 delete-expression 等效于调用析构函数和运算符 delete。 POD 类型也一样:

struct POD {
    int i;
    float f;
    char c;
};
POD* c = new POD[100];
void* c1 = reinterpret_cast<void*>(c);
operator delete[](c1);

标量和 POD 类型的另一件事是使用 operator new 分配并使用 delete 表达式解除分配:

void* d = operator new[](sizeof(unsigned char) * 100);
unsigned char* d1 = reinterpret_cast<unsigned char*>(d);
delete[] d1;

void* e = operator new[](sizeof(short) * 100);
short* e1 = reinterpret_cast<short*>(e);
delete[] e1;

void* f = operator new[](sizeof(POD) * 100);
POD* f1 = reinterpret_cast<POD*>(f);
delete[] f1;

所有这些示例似乎都是格式正确的,但我没有设法找到它是否真的如此。有人可以确认或告诉我我错在哪里吗?特别是我担心所有这些类型都有不同的对齐方式,应该将其传递给 operator new/delete:

alignof(unsigned char) 1
alignof(short) 2
对齐(POD)4


UPD。有些人似乎将此问题与Is it safe to delete a void pointer? 混淆了。我问的是如何将expression newoperator deleteoperator newexpression delete 混合使用。根据标准表达形式是用运算符形式实现的。问题是混合它们对于标量和 POD 类型的有效性如何?

【问题讨论】:

  • 如果他们没问题,我会感到非常惊讶。我的(受过教育?)猜测说这很可能是 UB。
  • @HostileFork 这个问题解决了一个完全不同的情况
  • 哪种情况?正如现在所制定的那样,HostileFork 是正确的。
  • 究竟是什么保证new int[10]返回的指针与它从内部调用operator new[](whatever)获得的指针相同,并且whatever == 10*sizeof(int)

标签: c++ memory new-operator delete-operator


【解决方案1】:

它根本无效。当您使用析构函数分配对象数组时,运行时必须记住要调用析构函数的对象数量。最简单的方法是在数组之前分配一些额外的内存,并将计数存储在那里。这意味着如果您使用析构函数分配 3 个单字节对象,operator new 将被要求(例如)11 个字节的内存 - 其中 3 个保存对象,8 个保存计数(在 size_t 中)。

operator delete 想要那个 11 字节内存块的地址 - 而不是保存/持有三个对象的 3 字节块的地址。

现在,我知道您在询问没有析构函数的内置类型 - 关键是,运行时库很可能会选择分配计数 无论如何简单。

【讨论】:

    【解决方案2】:

    我认为将 new-expressionsdelete-expressions 与调用(甚至是关联的)allocation 函数deallocation 函数配对——如operator delete[]() 等——导致未定义的行为。据我所知,前者可能会做一些额外的内务处理,而后者则在“原始内存”上运行。

    这是来自 ISO/IEC 14882:2014 第 5.3.5 条,它似乎明确说明了这一点(由我自己设置为粗体):

    在第一种选择(delete object)中,delete 的操作数的值可能是一个空指针值,一个指向前一个 创建的非数组对象的指针new-expression,或指向表示此类对象的基类的子对象(1.8)的指针(第 10 条)。 如果不是,则行为未定义。在第二种选择(delete array)中,delete 的操作数的值可以是空指针值或由前一个数组 new-expression 产生的指针值。81如果不是,则行为未定义。 [ 注意: 这意味着 delete-expression 的语法必须与 new 分配的对象的类型匹配,而不是 new- 的语法表达式。 — 尾注]

    【讨论】:

      【解决方案3】:

      有些人似乎将此问题与Is it safe to delete a void pointer? 混淆了。

      (^-- 答案是“,但它可能会偶然地起作用……在某些情况下”。)

      我在询问是否将表达式 new 与 operator delete 和 operator new 与表达式 delete 混合使用。

      ...如果它是合法的,可能会很好地回答上述问题,不是吗?

      "Nope, you can't use ordinary `delete[]`...but you can use `operator delete[]`.
      Its type signature takes a `void*` instead of a typed pointer."
      

      但这不是答案(如果是这样的话,编译器会显得有点固执,不只是为你做这件事。)无论哪种方式,问题在于只给一个 void 指针而没有类型信息,编译器对于特定于new 中给出的类型的任何“构建”,不一定具有正确的“拆卸”代码。

      (如果您认为为基本类型做一些特殊或不同的事情可能不有趣,那么如果您有一个地址清理器或未定义行为清理器类型的工具想要投入一些东西呢? short 的特殊用途,其测量方式与其他类型不同...可能是因为您特别要求对短裤进行不同的测量?)

      由于普通的删除是根据operator delete[] 实现的,所以没有额外的魔力……更少了!

      因此答案几乎相同。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-08-04
        • 2014-03-26
        • 2011-06-11
        • 1970-01-01
        • 2013-03-12
        • 1970-01-01
        • 2021-06-20
        相关资源
        最近更新 更多