【问题标题】:Why the overriden `operator new` isn't called?为什么不调用覆盖的`operator new`?
【发布时间】:2012-01-05 15:11:51
【问题描述】:

我在VS2005中运行如下代码:

#include <iostream>
#include <string>
#include <new>
#include <stdlib.h>

int flag = 0;

void* my_alloc(std::size_t size)
{
    flag = 1;
    return malloc(size);
}

void* operator new(std::size_t size) { return my_alloc(size); }
void operator delete(void* ptr) { free(ptr); }
void* operator new[](std::size_t size) { return my_alloc(size); }
void operator delete[](void* ptr) { free(ptr); }

int main()
{
    std::string str;
    std::getline(std::cin, str);
    std::cout << str;
    return flag;
}

我输入了一个足够长的字符串(比小字符串优化缓冲区长):

0123456789012345678901234567890123456789012345678901234567890123456789

在 Debug 编译中进程返回 1,在 Release 配置中进程返回 0,这意味着 new 运算符未被调用!我可以通过设置断点、写入输出/调试输出等来验证这一点......

为什么会这样,这是符合标准的行为吗?

【问题讨论】:

  • 您是否尝试过调试和单步调试代码而不是依赖输出?
  • @Kolky:是的。例如,我在 operator new 中设置了一个全局标志,并从 main a -- 解决方案返回它,根本没有输出。
  • 有一个interesting bug in GCC <= 4.6.2 在某些编译器设置下,string 的分配器将使用替换的分配函数。因此,这并非完全闻所未闻。为了好玩,请尝试不同的数据结构,例如 vector
  • @KerrekSB:很有趣。是的,我知道它适用于矢量。这确实是一个错误。看我的回答。

标签: c++ visual-studio-2005 operator-overloading new-operator


【解决方案1】:

经过一番研究,@bart-jan 在他们的另一个答案(现已删除)中写的实际上是正确的。

很容易看出,在 Release 中根本没有调用 mine 操作符,而是调用了 CRT 版本。 (不,对于所有在黑暗中拍摄的人来说,这里没有递归。)问题是“为什么”?

以上是针对动态链接的 CRT(这是默认值)编译的。 Microsoft 在 CRT DLL 中提供了std::string(在许多其他标准模板中)的实例化。查看 VS2005 附带的 Dinkumware 头文件:

#if defined(_DLL_CPPLIB) && !defined(_M_CEE_PURE)

template class _CRTIMP2_PURE allocator<char>;
// ...
template class _CRTIMP2_PURE basic_string<char, char_traits<char>,
    allocator<char> >;

其中_CRTIMP2_PURE 扩展为__declspec(dllimport)。这意味着在 Release 中,链接器将 std::string 链接到 CRT 构建时实例化的版本,该版本使用 new 的默认实现。

目前尚不清楚为什么它不会在调试中发生。正如@Violet Giraffe 猜对的那样,它一定会受到一些开关的影响。但是,我认为这是链接器开关,而不是编译器开关。我无法准确确定哪个开关很重要。

其他答案忽略的剩余问题是“它是否标准”?尝试VS2010中的代码,无论我编译什么配置,它确实调用了我的operator new!查看 VS2010 附带的头文件,发现 Dinkumware 删除了上述实例化的 __declspec(dllimport)。因此,我相信旧行为确实是编译器错误,并且不是标准。

【讨论】:

  • 这听起来像是一个链接器/库错误,不允许有条件地替换函数的默认版本。 GCC 称之为“弱引用”; MSVC 必须有类似的概念。
  • @KerrekSB:不完全是。您不能在 DLL 的上下文中执行此操作。包含 std::string 实现的 DLL 已经与一些 new 实现链接,以后操作系统加载它时不能重新绑定它。
【解决方案2】:

你有无限递归。

相反,喜欢

#include <iostream>
#include <string>
#include <new>
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>

void* my_alloc( char const* str, std::size_t size )
{
    fprintf( stderr, "%s %lu\n", str, (unsigned long)size );
    return malloc( size ); // I don't care for errors here
}

void* operator new( std::size_t size )      { return my_alloc( "new", size ); }
void operator delete( void* ptr )           { free(ptr); }
void* operator new[]( std::size_t size )    { return my_alloc( "new[]", size ); }
void operator delete[]( void* ptr )         { free( ptr ); }

int main()
{
    std::string str;
    std::cout << "? ";
    std::getline(std::cin, str);
    std::cout << str;
}

【讨论】:

  • 同样的结果。请参阅上面的编辑以获取没有递归的版本(而且我也没有递归)。
  • 我确实使用 Visual C++ 10 进行了检查。除了您自己的原始错误之外,您还可能遇到编译器错误。但我认为这不太可能;最有可能是 PEBCAK。
  • 请解释一下你在第二个代码 sn-p 中看到递归的地方。顺便说一句,我也没有看到/观察到第一个片段中的递归。
  • (可能的)无限递归在您的原始代码中,而不是第二个 sn-p。对于第二个 sn-p 考虑你很可能'做错了什么。就像运行错误的exe一样。第二个代码的问题在 Visual C++ 10.0 中没有重现
  • 我一拿到VS2010的电脑就去检查一下。
【解决方案3】:

您确实应该逐步调试代码。您是否尝试在发布配置中设置 /Od 以禁用优化?我想知道这是否会改变行为。

【讨论】:

  • 试过了。它不会改变任何东西。同上。
  • @ybungalobill:好的,链接到调试运行时怎么样?尝试 /MDd 而不是 /MD。此外,您现在可以尝试单步执行代码以查看哪个函数拦截了控件。也许,有一些宏,只在发布中定义?
  • 调试库中没有优化:相同的结果。
  • @ybungalobill:当你进入new时你看到了什么?
  • 嗯,我的新版本只是在发布时没有被编译。为正确的方向 +1。
【解决方案4】:

从您的新运算符或其中一个被调用函数调用 cout

【讨论】:

  • std::ostream
  • @Violet:我没有。 const char* 不是 std::string
  • @bert-jan:首先我认为它不能符合标准。其次,即使没有任何输出,结果也是可重现的。
  • 如果你实例化 std::string() 或使用 std::cout
  • 也许在这种情况下,std::string 关联分配器决定不在发布版本中使用 new(),但这是一个很远的猜测
猜你喜欢
  • 2010-09-23
  • 1970-01-01
  • 2021-08-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多