操作员删除签名意外行为

时间:2014-07-16 00:05:16

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

在他的“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参数以确定正确的对象大小,特别是在删除基指针指向的派生对象时(base需要虚拟dtor以便传递正确的大小)。

我试图重载单个对象的全局版本。操作员新的工作正常。使用上述签名的操作员删除工作正常,但不会调用删除。如果我更改删除签名以便它只需要一个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)似乎都有效!

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

2 个答案:

答案 0 :(得分:1)

来自C ++ 1y草案:

  

5.3.5删除[expr.delete]

     

[...]
  11执行delete-expression时,应调用所选的释放函数,并将要回收的存储块的地址作为其第一个参数,并且(如果使用双参数解除分配函数)将块的大小作为其第二个论点.83

     

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

  

17.6.4.6替​​换函数[replacement.functions]

     

1第18至30条和附录D描述了C ++标准库定义的众多函数的行为。但是,在某些情况下,某些功能描述也适用于程序中定义的替换功能(17.3)   2 C ++程序可以为标题<new>(3.7.4,18.6)中声明的12个动态内存分配函数签名中的任何一个提供定义:

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

答案 1 :(得分:1)

在C ++ 11中,非成员void operator delete(void*, size_t)是&#34;展示位置释放,附加参数&#34;。它对应于具有附加参数的放置分配(如果您定义了一个):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 ++之间的一个重大变化。

根据N3797,在C ++中,如果你替换operator delete(void *),那么你也必须替换operator delete(void *, size_t),反之亦然。 (否则,据推测,该程序是不正确的。)

同样根据N3797,可以调用这两个函数中的任何一个来解除分配,但是我没有看到在哪种情况下调用哪个函数的明确规范。

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