为什么运算符 new 不被迫将论证视为"常量size_t"?

Why isn't operator new forced to take argument as "const size_t"?

本文关键字:常量 size new 运算符 被迫 为什么      更新时间:2023-10-16

没有在任何地方使用下面的东西,但这个问题仍然在我的脑海中徘徊了很长时间。

void* operator new (size_t size)
{
  // distort `size` to other value
  return malloc(size);
}

知道以上可能不是重载new的良好定义行为(如果size减少),为什么标准不强制编译器将其作为void* operator new (const size_t); ?

同样可以为operator delete也争论,它应该void operator delete (void* const);(添加const以避免指针被更改)。

void* operator new (const size_t n);
void* operator new (size_t n);

这两个是operator new的完全相同的声明。唯一的区别是,如果它是一个定义,则允许或不允许在函数体内修改n(这与调用者无关,因为参数是按值传递的)。因为这是一个实现细节,所以标准没有讨论它。

请记住,顶层const在函数声明中被忽略。它们只在定义中相关

const std::size_t和普通std::size_t的参数类型都是std::size_t。标准规定,在声明(后面的定义)中处理函数签名时,从类型中删除const。具体的段落见§8.3.5/3:

[…在生成参数类型列表后,对这些类型进行若干转换以确定函数类型。任何修改参数类型的cv限定符都将被删除。[示例:类型void()(const int)变为void()(int) -end示例]

现在,在函数的定义中,const确实具有您提出的效果:阻止您修改实参。您可以自由地使用const std::size_t参数来实现operator new。另一方面,强制执行基本上是无用的。正如其他人提到的,实参是const的事实可以通过将其复制到另一个变量并使用该变量来改变。当它不能给语言带来真正的价值时,给编译器增加额外的验证负担是没有意义的。

好,让我们假设它是const:

void* operator new (const size_t size)
{
   size_t alteredSize = size;
   alteredSize += 10; // for whatever reason
   return malloc( alteredSize );
}

完全相同
void* operator new (size_t size)
{
   size += 10;
   return malloc( size );
}

const对复制到函数中的参数(按值传递)不强制使用该值——函数可以创建一个可写副本并修改该副本。

非引用参数的Const-ness只是次要的。

标准从调用者的角度定义了"重要"的事情。

这些按值参数的一致性不会也不能改变使用它们的任何代码。实现者可以自己照顾自己。

更新:如果你愿意,你可以这样写

int f (const int);
int f (int) {return 0;}
int main () {
    int i;
    f (i);
}

上面我没有得到任何错误。继续创建size_t:-)

预料到反对意见:这里f代表operator new或其他什么。所以我们有一个矛盾的声明/定义,但编译器没有抱怨。为什么?因为这并不重要

您已经在operator new上收到了许多很好的答案。对于不是使size参数为operator new const的另一个参数:重写实现很可能希望分配比请求更多的空间,例如cookie,保护字等。

operator delete也可以采用同样的方法,它应该void operator delete (void* const);(添加const以避免指针被更改)。

在大多数使用虚拟内存的现代操作系统上,free不一定会释放分配给操作系统的内存。这些内存很可能仍然在程序的虚拟空间中。迂腐的重写很可能希望在删除指向内存之前用绝对垃圾填充它,以进一步使调用函数随后对该内存的使用无效。调用函数在删除指向内存后不能使用它,那么为什么要用不必要的限制来限制operator delete呢?