尝试包装和覆盖全局运算符 new 时的递归调用

发布于 2024-08-14 07:29:21 字数 2056 浏览 4 评论 0原文

我已经有一段时间没有编写 C++ 程序了,并且在使用重载的全局运算符 newdelete 时遇到了奇怪的行为。 问题的本质似乎是围绕默认全局 new 构建的包装器并驻留在单独的源文件中 尽管如此,调用另一个(并且单独编译的)源文件中重载的operator new

为什么会这样,即我违反/滥用了哪些语言规则/功能?

预先感谢,详细信息如下。

项目结构:

.
..
main.cpp
mem_wrappers.h
mem_wrappers.cpp

项目文件内容:

main.cpp

#include "./mem_wrappers.h"
#include <iostream>
#include <cstring>


void* operator new[] (size_t sz) throw (std::bad_alloc) {
  std::cout << "overloaded new()[]" << std::endl;
    return default_arr_new_wrapper(sz);
}


int main() {
    const unsigned num = 5;
    int * i_arr = new int [num];

    return 0;
}

mem_wrappers.h

#include <cstring>


void * default_arr_new_wrapper(size_t sz);

mem_wrappers.cpp

#include <new>
#include <cstring>
#include <iostream>


void * default_arr_new_wrapper(size_t sz) {
    std::cout << "default_arr_new wrapper()" << std::endl;
    return ::operator new[](sz);
}

符合 g++ main.cpp mem_wrappers.cpp - -ansi --pedantic -Wall 运行时会向 default_arr_new_wrapper 提供无尽的 operator new[] ,反之亦然,调用会产生以下输出:

overloaded new()[]

default_arr_new_wrapper()

overloaded new()[]

default_arr_new_wrapper()

...

最后, SO(MS Visual Studio Express 编译器的行为类似)。

我使用的编译器:gcc 版本 3.4.2(mingw-special)MS Visual Studio 2008 版本 9.0.30729.1 SP

编辑/更新

如果(如第一个答案和评论所暗示的那样)仅通过在单个编译单元中重载它[操作符],就可以有效地为整个可执行文件重新定义全局operator new,那么:

是吗?仅仅链接一个目标文件,该文件的源重载了全局operator new,就会使任何应用程序或库更改其(好吧,我们这样称呼它)内存分配策略?因此,这个重载运算符是否有效地为语言运行时提供了一个钩子(我的意思是已经编译的没有任何重载的新对象文件中的那些链接器符号是什么,是不是只能有一个 new< /代码>)?

PS 我知道 mallocfree 可以,并且在发布到这里之前已经尝试过它们(工作正常),但是尽管如此,这种行为背后的原因是什么(如果我实际上是无论如何都要包装默认的operator new:))?

I have not programmed C++ for a while and encountered a strange behavior when toying with overloaded global operators new and delete.
The essence of the problem seems to be that a wrapper build around the default global new and residing in a separate source file
nevertheless calls an operator new overloaded in another (and separately compiled so) source file.

Why is it so, i.e. which language rules / features am I violating / misusing?

Thanks in advance, the details are below.

Project structure:

.
..
main.cpp
mem_wrappers.h
mem_wrappers.cpp

Project files contents:

main.cpp

#include "./mem_wrappers.h"
#include <iostream>
#include <cstring>


void* operator new[] (size_t sz) throw (std::bad_alloc) {
  std::cout << "overloaded new()[]" << std::endl;
    return default_arr_new_wrapper(sz);
}


int main() {
    const unsigned num = 5;
    int * i_arr = new int [num];

    return 0;
}

mem_wrappers.h

#include <cstring>


void * default_arr_new_wrapper(size_t sz);

mem_wrappers.cpp

#include <new>
#include <cstring>
#include <iostream>


void * default_arr_new_wrapper(size_t sz) {
    std::cout << "default_arr_new wrapper()" << std::endl;
    return ::operator new[](sz);
}

Complied with g++ main.cpp mem_wrappers.cpp --ansi --pedantic -Wall when run gives endless operator new[] to default_arr_new_wrapper and vice versa calls resulting in the following output:

overloaded new()[]

default_arr_new_wrapper()

overloaded new()[]

default_arr_new_wrapper()

...

and, finally, in SO (the MS Visual Studio Express compiler behaves alike).

Compilers I use: gcc version 3.4.2 (mingw-special) and MS Visual Studio 2008 Version 9.0.30729.1 SP.

EDIT / UPDATE

If (as the first answers and comments suggest) the global operator new is effectively redefined for the whole executable just by overloading it [the operator] in a single compilation unit, then:

is it that merely linking with an object file whose source overloads the global operator new makes any application or library change its (well, lets call it so) memory allocation policy? Is thus this overload operator efficiently providing a hook for the language runtime (I mean what's with those linker symbols in the already-compiled-without-any-overloaded-new object files, is it that there can be only one new)?

P.S. I know that malloc and free would do, and already tried them before posting here (worked ok), but nevertheless, what's behind this behavior (and what if I was actually to wrap the default operator new anyway? :)) ?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

要走就滚别墨迹 2024-08-21 07:29:21

如果您阅读了标准中的措辞,那么当您定义自己的全局operator new函数时,您所做的称为<​​em>替换(而不是重载并且不覆盖)。大多数时候,当人们谈论更改全局 operator new 功能时,他们使用术语“重载”并将 new 称为“操作符”。当结果行为与操作符重载的通常预期不一致时,这通常会导致混乱。这显然就是你的情况所发生的情况。您期望重载行为,但您真正得到的是替换:一旦您在某个翻译单元中定义了全局operator new函数的替换版本,它就会适用于整个程序。 (new 并不是真正的运算符。)

顺便说一句,替换 是那些罕见的“不公平”语言功能之一,从某种意义上说,它并不普遍可用给用户。您无法在 C++ 中编写“可替换”函数。您编写的任何内容都不能与全局 operator newoperator new 的默认库实现的行为方式相同。 (替换是弱符号链接器概念的语言级表现)。

If you read the wording in the standard, what you do to global operator new function when you define your own is called replacement (not overloading and not overriding). Most of the time when people talk about changing the global operator new functionality they use the term "overloading" and refer to new as an "operator". This often leads to confusion, when the resultant behavior does not agree with what is normally expected from operator overloading. This is apparently what happens in your case. You expect overloading behavior, but what you really get is replacement: once you defined one replacement version of global operator new function in some translation unit it works for the entire program. (And new is not really an operator.)

As a side note, replacement is one of those rare "unfair" language features, in a sense that it is not generally available to the user. You can't write "replaceable" functions in C++. You can't write anything that would behave the way default library implementations of global operator new and operator new behave. (Replacement is a language-level manifestation of the linker concept of a weak symbol).

白况 2024-08-21 07:29:21

我认为 Neil 的评论之后没有太多需要添加的内容,但如果您重载全局运算符 new,则无法仅对一个翻译单元执行此操作,new 将为整个可执行文件或 dll 重载。

正如您所注意到的,从您自己的重载全局运算符 new 中调用全局运算符 new ,您将获得无休止的递归(好吧,至少直到您用完堆栈空间;)

如果您想玩弄这个,请尝试使用 malloc 和 free 在你的包装器中启动并运行。

如果运算符仅位于一个翻译单元的本地,那么如果您将分配有一个版本的 new 的对象传递给另一个翻译单元,并尝试使用另一个不匹配的对象删除它,那么您会遇到麻烦。运算符删除

C++ 标准第 18.4 节介绍了运算符 newdelete 的详细信息。

I don't think there's much to add after Neil's comment but if you overload global operator new, you can't do it for just one translation unit, new will be overloaded for the entire executable or dll.

As you've noticed calling global operator new from within your own overloaded global operator new you will get endless recursion (well, until you run out of stack space at least ;)

If you want to toy around with this try using malloc and free inside your wrapper to get up and running.

If the operators were only local to one translation unit you would run into trouble if you passed an object which were allocated with one version of new to another translation unit and tried to delete it with another, non matching, operator delete.

The details of operator new and delete are covered in section 18.4 of the C++ standard.

天涯离梦残月幽梦 2024-08-21 07:29:21

为了扩展尼尔的评论,“全局”new 可能不会被赋予static存储类说明符,因此是“全局”的 - 它对模块外部的链接器是可见的(可以是 extern'd例如,来自任何其他模块)。

无效:

static void* operator new(size_t sz) {}

有效:

//in main.cpp
void* operator new(size_t sz) {}

//in foo.cpp
extern void* operator new(size_t sz);

根据您想要执行的操作,最好的方法可以是在您的定义中使用 malloc/new,或者向您的覆盖添加一个参数,以便签名发生更改(然后您可以回退到默认的全局 new,而无需递归)。

不要忘记在分配失败时抛出异常,并实现重载的无抛出版本。

To expand on Neil's comment, "global" new may not be given the static storage class specifier, and is therefore "global" - it is visible to the linker outside of the module (could be extern'd from any other module, for instance).

Invalid:

static void* operator new(size_t sz) {}

Valid:

//in main.cpp
void* operator new(size_t sz) {}

//in foo.cpp
extern void* operator new(size_t sz);

Depending on what you want to do, the best approach can be to use malloc/new in your definitions, or add a parameter to your override so the signature changes (you can then fall-back to the default global new without recursion).

Don't forget about throwing an exception in case of allocation failure, and implementing no-throw versions of your overloads.

无可置疑 2024-08-21 07:29:21

放大 AndreyT 所说的内容:

当您编写自己的全局 operator new 函数时,您提供了链接器可以看到的定义。当谈到链接时,链接器将需要找到 new 的定义,因为它被频繁使用。它开始在您要求它链接的目标文件中搜索,嘿,很快它就在那里,所以它可以停止搜索。

如果您没有提供定义,它将继续搜索您的任何其他目标文件,然后搜索您提供的库,最后搜索编译器附带的运行时,该编译器必须提供一个或不提供任何链接。

To amplify what AndreyT has said:

When you write your own global operator new function you provide a definition that the linker can see. When it comes to link time, the linker will need to find a definition for new, just because its used a lot. It starts searching in the object files that you are asking it to link, and hey-presto there it is, so it can stop searching.

If you had not provided a definition, it would have carried on searching through any other object files of yours, then through libraries you provide, and finally through the run-time that comes with your compiler, which has to provide one or nothing would link.

节枝 2024-08-21 07:29:21

查看 GNU C++ 标准库,您会看到类似这样的装饰,意思是“弱链接”:

_GLIBCXX_WEAK_DEFINITION void *
operator new (std::size_t sz) throw (std::bad_alloc)

Looking in the GNU C++ standard lib, you see decorations like this one, meaning "weak linking":

_GLIBCXX_WEAK_DEFINITION void *
operator new (std::size_t sz) throw (std::bad_alloc)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文