尝试包装和覆盖全局运算符 new 时的递归调用
我已经有一段时间没有编写 C++ 程序了,并且在使用重载的全局运算符 new
和 delete
时遇到了奇怪的行为。 问题的本质似乎是围绕默认全局 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 我知道 malloc
和 free
可以,并且在发布到这里之前已经尝试过它们(工作正常),但是尽管如此,这种行为背后的原因是什么(如果我实际上是无论如何都要包装默认的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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
如果您阅读了标准中的措辞,那么当您定义自己的全局
operator new
函数时,您所做的称为<em>替换(而不是重载并且不覆盖)。大多数时候,当人们谈论更改全局operator new
功能时,他们使用术语“重载”并将new
称为“操作符”。当结果行为与操作符重载的通常预期不一致时,这通常会导致混乱。这显然就是你的情况所发生的情况。您期望重载行为,但您真正得到的是替换:一旦您在某个翻译单元中定义了全局operator new
函数的替换版本,它就会适用于整个程序。 (new
并不是真正的运算符。)顺便说一句,替换 是那些罕见的“不公平”语言功能之一,从某种意义上说,它并不普遍可用给用户。您无法在 C++ 中编写“可替换”函数。您编写的任何内容都不能与全局
operator new
和operator 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 globaloperator new
functionality they use the term "overloading" and refer tonew
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 globaloperator new
function in some translation unit it works for the entire program. (Andnew
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
andoperator new
behave. (Replacement is a language-level manifestation of the linker concept of a weak symbol).我认为 Neil 的评论之后没有太多需要添加的内容,但如果您重载全局运算符 new,则无法仅对一个翻译单元执行此操作,
new
将为整个可执行文件或 dll 重载。正如您所注意到的,从您自己的重载全局运算符 new 中调用全局运算符 new ,您将获得无休止的递归(好吧,至少直到您用完堆栈空间;)
如果您想玩弄这个,请尝试使用 malloc 和
free
在你的包装器中启动并运行。如果运算符仅位于一个翻译单元的本地,那么如果您将分配有一个版本的 new 的对象传递给另一个翻译单元,并尝试使用另一个不匹配的对象删除它,那么您会遇到麻烦。运算符
删除
。C++ 标准第 18.4 节介绍了运算符
new
和delete
的详细信息。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
andfree
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, operatordelete
.The details of operator
new
anddelete
are covered in section 18.4 of the C++ standard.为了扩展尼尔的评论,“全局”new 可能不会被赋予
static
存储类说明符,因此是“全局”的 - 它对模块外部的链接器是可见的(可以是 extern'd例如,来自任何其他模块)。无效:
有效:
根据您想要执行的操作,最好的方法可以是在您的定义中使用 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:
Valid:
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.
放大 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 fornew
, 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.
查看 GNU C++ 标准库,您会看到类似这样的装饰,意思是“弱链接”:
Looking in the GNU C++ standard lib, you see decorations like this one, meaning "weak linking":