【问题标题】:Operator delete signature unexpected behavior [duplicate]操作员删除签名意外行为[重复]
【发布时间】:2014-07-16 00:05:16
【问题描述】:

在他的《C++ 编程语言》(第 4 版)一书中,stroustroup 提到可以通过编写具有以下签名的全局函数来重载全局运算符 new & delete:

void* operator new(size_t);               // use for individual object
void* operator new[](size_t);             // use for array
void operator delete(void*, size_t);      // use for individual object
void operator delete[](void*, size_t);    // use for array

注意: size_t 参数传递给删除以确定正确的对象大小,特别是在删除由基指针指向的派生对象时(基需要虚拟 dtor 以便传递正确的大小)。

我试图重载单个对象的全局版本。运算符 new 工作正常。具有上述签名的操作员删除工作正常,但删除不会被调用。如果我更改删除签名以便它只需要一个 void *,它就会被调用。可能是什么问题:

代码如下:

void * operator new (size_t size)
{
    cout << "My operator new called\n";
    auto p = malloc(size);
    return p;
}

void operator delete (void * ptr, size_t size) // Removing size_t parameter makes it work
{
    cout << "My operator delete called\n";
    free(ptr);
}

奇怪的是,如果我让操作员删除一个类的成员,使其仅为该类重载,那么删除签名(带有 size_t 和不带 size_t)似乎都可以工作!

在 delete 中传递 size_t 参数似乎是合乎逻辑的,正如我提到的 NOTE 中所解释的那样。但这种行为的原因可能是什么?我正在使用 VS2013 来测试示例。

【问题讨论】:

  • 感谢网络链接。那里的解释似乎表明删除的全局版本具有签名,该签名需要两个 void *,而特定于类的版本需要一个 void * 和一个 size_t。这与书中提到的相反。那么你是在暗示这是书中的一个错误吗?
  • 仔细阅读。这些是您从帖子中选择的展示位置版本。
  • 查看此操作员delete可能的签名:en.cppreference.com/w/cpp/memory/new/operator_delete
  • @Deduplicator Aaah...是的...所以全局的不需要 size_t。所以我想这本书有一个错误

标签: c++ operator-overloading new-operator


【解决方案1】:

来自 C++1y 草案:

5.3.5 删除[expr.delete]

[...]
11 执行删除表达式时,应调用选定的释放函数,将要回收的存储块的地址作为其第一个参数,并且(如果使用双参数释放函数)块的大小作为其第二个论点。83

脚注83)如果要删除的对象的静态类型是完整的,并且不同于动态类型,并且析构函数不是 virtual,大小可能不正确,但如上所述,这种情况已经未定义。

17.6.4.6 替换函数[replacement.functions]

1 第 18 到 30 条和附录 D 描述了 C++ 标准库定义的众多函数的行为。然而,在某些情况下,这些函数描述中的某些也适用于程序 (17.3) 中定义的替换函数。
2 C++ 程序可以为头文件&lt;new&gt; (3.7.4, 18.6) 中声明的十二个动态内存分配函数签名中的任何一个提供定义:

operator new(std::size_t)
operator new(std::size_t, const std::nothrow_t&)
operator new[](std::size_t)
operator new[](std::size_t, const std::nothrow_t&)
perator delete(void*)
operator delete(void*, const std::nothrow_t&)
operator delete[](void*)
operator delete[](void*, const std::nothrow_t&)

我的注释:接下来的四个是 C++1y 中的新内容

operator delete(void*, std::size_t)
operator delete(void*, std::size_t, const std::nothrow_t&)
operator delete[](void*, std::size_t)
operator delete[](void*, std::size_t, const std::nothrow_t&)

3 使用程序的定义而不是实现 (18.6) 提供的默认版本。这种替换发生在程序启动之前(3.2、3.6)。程序的定义不应指定为内联。无需诊断。

还可以看看在 C++1y 中引入大小释放的提案:
http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2013/n3536.html

【讨论】:

    【解决方案2】:

    在 C++11 中,非成员 void operator delete(void*, size_t) 是“带有附加参数的放置解除分配”。它对应于带有附加参数的布局分配(如果您定义了一个):void *operator new(size_t, size_t)

    澄清一下,根据3.7.4.2,T::operator delete(void*, size_t)是一个普通的释放函数,但是N3337并没有说::operator delete(void *, size_t)是一个普通的释放函数;事实上,::operator delete 的签名并没有出现在文档的任何地方。具体来说,17.6.4.6 并未将其列在全局版本中。

    在 C++1y 中,::operator delete(void*, size_t) 是一个常用的释放函数(即非放置)。在我看来,这是 C++11 和 C++1y 之间的重大变化。

    根据 N3797,在 C++1y 中,如果您替换 operator delete(void *),那么您也必须替换 operator delete(void *, size_t),反之亦然。 (否则,可能是程序格式错误)。

    同样根据 N3797,这两个函数中的任何一个都可能被调用以进行释放,但我没有看到一个明确的说明,说明在什么情况下调用了哪个函数。

    注意:重载是错误的术语;当您定义一个通常的分配运算符时,它替换标准库版本。

    【讨论】:

      猜你喜欢
      • 2011-01-01
      • 1970-01-01
      • 2013-02-07
      • 1970-01-01
      • 2013-06-25
      • 1970-01-01
      • 2017-01-08
      • 2017-09-08
      相关资源
      最近更新 更多