You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

关于C++全局operator new重载与替换的困惑

C++全局operator new的重载与替换疑惑

一、符合预期的重载场景

先看这段代码,实现了带额外参数的全局operator new:

#include <iostream>

using namespace std;

void *operator new( size_t n, const string &pat ) {
    char *p = static_cast<char *>(::operator new( n ));
    const char *pattern = pat.c_str();
    if( !pattern || !pattern[0] )
        pattern = "\0"; // note: two null chars
    const char *f = pattern;
    for( int i = 0; i < n; ++i ) {
        if( !*f )
            f = pattern;
        p[i] = *f++;
    }
    cout<<"overload!"<<endl;
    return p;
}

int main() {
    string fill( "<garbage>" );
    string *string1 = new string( "Hello" ); // 调用标准版本
    string *string2 =
            new (fill) string( "World!" ); // 调用重载版本,因为fill作为额外参数匹配重载的参数列表
    cout<<string1<<" "<<string2<<endl;
    return 0;
}

这段代码的行为符合普通函数重载逻辑:我们定义的operator new多了一个const string&参数,属于标准版本的重载。当使用new (fill)这种定位new语法时,编译器会匹配这个带额外参数的版本;而普通的new表达式则调用标准库的全局operator new,其签名如下:

// from new.h
void* operator new(std::size_t) _GLIBCXX_THROW (std::bad_alloc)
  __attribute__((__externally_visible__));

二、超出预期的行为:同签名的"重载"

但当我们添加一个和标准签名完全一致的全局operator new(即overload2)时,情况就变了:

#include <iostream>

using namespace std;

// overload1:带额外参数的重载版本
void *operator new( size_t n, const string &pat ) {
    char *p = static_cast<char *>(::operator new( n ));
    const char *pattern = pat.c_str();
    if( !pattern || !pattern[0] )
        pattern = "\0"; // note: two null chars
    const char *f = pattern;
    for( int i = 0; i < n; ++i ) {
        if( !*f )
            f = pattern;
        p[i] = *f++;
    }
    cout<<"overload1"<<endl;
    return p;
}

// overload2:和标准版本参数完全一致
void* operator new(size_t n){
    cout<<"overload2"<<endl;
    return malloc(n);
}

int main() {
    string fill( "<garbage>" );
    string *string1 = new string( "Hello" ); // 调用overload2而非标准版本
    string *string2 =
            new (fill) string( "World!" ); // overload1内部调用的也是overload2
    cout<<string1<<" "<<string2<<endl;
    return 0;
}

运行后会发现,不仅普通new表达式调用了overload2,连overload1里显式写的::operator new(n)也没有调用标准版本,而是指向了overload2——这完全不符合普通重载的规则,因为普通重载要求参数列表不同,同签名的函数会报重定义错误,但这里却能正常编译且覆盖了标准版本。

结合<new>头文件的注释:

These are replaceable signatures:

  • normal single new and delete (no arguments, throw bad_alloc on error)
  • normal array new and delete (same)
  • nothrow single new and delete (take a nothrow argument, return NULL on error)
  • nothrow array new and delete (same)
    Placement new and delete signatures (take a memory address argument, does nothing) may not be replaced by a user's program.

这里的核心疑问是:overload2到底是替换了标准版本,还是仅仅是重载?为什么它的行为和普通重载相悖,但大家仍习惯称之为"重载"?

三、本质:特殊替换而非普通重载

从C++标准的语义来讲,overload2属于替换标准库的可替换函数,而非普通重载:

  1. 普通重载的前提是参数列表不同,而overload2和标准版本的签名完全一致,不符合重载的定义。如果是普通函数,同签名的全局函数会触发重定义错误,但operator new的这些特定签名是标准允许用户替换的特殊情况。
  2. 替换的语义是覆盖:当你在全局作用域定义了这些可替换签名的operator new后,编译器会优先使用你的版本,甚至显式的::operator new也会绑定到你定义的版本——因为全局作用域下你的实现覆盖了标准库的版本。
  3. 带额外参数的版本才是真正的重载:比如overload1这种带const string&参数的,属于普通重载,和标准版本共存,只有通过定位new语法(new (args))才会匹配调用。

至于为什么大家习惯把替换也叫"重载",其实是口语化的简化:因为自定义内存分配的场景中,替换标准签名和添加重载版本常常一起出现,久而久之就被混称为"重载全局operator new",但从标准语义上,这两者的本质完全不同。

内容的提问来源于stack exchange,提问作者Paoer111

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 04:52:44