sha*_*oth 8 c++ compiler-construction new-operator compiler-optimization
最近我在ideone.com上运行了以下代码(gcc-4.3.4)
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <new>
using namespace std;
void* operator new( size_t size ) throw(std::bad_alloc)
{
void* ptr = malloc( 2 * 1024 * 1024 * 1024);
printf( "%p\n", ptr );
return ptr;
}
void operator delete( void* ptr )
{
free( ptr );
}
int main()
{
char* ptr = new char;
if( ptr == 0 ) {
printf( "unreachable\n" );
}
delete ptr;
}
Run Code Online (Sandbox Code Playgroud)
得到这个输出:
(nil)
unreachable
Run Code Online (Sandbox Code Playgroud)
虽然new
永远不应该返回一个空指针,因此调用者可以依赖它,编译器可以消除ptr == 0
检查并将相关代码视为无法访问.
为什么编译器不会删除该代码?它只是一个错过的优化还是有其他原因?
我认为这很简单,你有两个根本不同的东西混淆:
malloc()可以返回任何内容,特别是零.
void * operator new(size_t) throw(std::bad_alloc)
标准要求全局C++分配函数返回指向所需存储量(+适当对齐)的指针,或以其他方式退出异常.
如果要替换全局分配功能,则您有责任提供符合标准规则的替换.最简单的版本如下所示:
void * operator new(size_t n) throw(std::bad_alloc) {
void * const p = std::malloc(n);
if (p == NULL) throw std::bad_alloc();
return p;
}
Run Code Online (Sandbox Code Playgroud)
任何严肃的实现实际上应该包含一个循环来调用已注册的new-handler直到分配成功,并且只有在没有新的处理程序时才抛出.
你写的程序很简单.
题外话:为什么这样new
定义?你说的时候考虑标准的分配顺序T * p = ::new T();
.它相当于:
void * addr = ::operator new(sizeof(T)); // allocation
T * p = ::new (addr) T(); // construction
Run Code Online (Sandbox Code Playgroud)
如果第二行抛出(即构造失败),则使用相应的释放函数释放存储器.但是,如果第一次调用失败,那么执行必须永远不会到达第二行!实现这一目标的唯一方法是通过异常退出.(分配函数的无抛出版本仅供手动使用,其中用户代码可以在继续构造之前检查分配器的结果.)
C++ 11在这个问题上很明确:
void* operator new(std::size_t size);
:... 3必需行为:将非空指针返回到适当对齐的存储(3.7.4),否则抛出bad_alloc
异常.此要求绑定在此函数的替换版本上.
你点击未定义的行为.
[编辑]现在,为什么这会妨碍优化?编译器供应商倾向于花时间梦想对常用的代码模式进行优化.对于他们来说,优化更快的未定义行为通常没什么好处.(某些UB可能在该特定编译器上定义良好并且仍然被优化,但上面的示例可能不会).
我认为你期待的优化器太多了.当优化器到达此代码时,它认为new char
只是另一个函数调用,其返回值存储在堆栈中.所以它并不认为这种if
情况值得特别对待.
这可能是因为你覆盖了这个事实operator new
,并且它超出了优化器的工资等级来查看那里,看到你调用malloc
,它可以返回NULL
,并决定这个被覆盖的版本不会返回NULL
.malloc
看起来像Just Another Function Call.谁知道?您也可以链接自己的版本.
有覆盖运营商的改变C++的行为其他几个例子:operator &&
,operator ||
,和operator ,
.当不被覆盖时,每个都有一个特殊的行为,但在被覆盖时表现得像标准运算符.例如,如果左侧评估为,operator &&
则根本不会计算其右侧false
.但是,如果重写,双方的operator &&
计算之前经过他们operator &&
; 短路功能完全消失.(这样做是为了支持使用运算符重载来定义C++中的迷你语言;有关此示例,请参阅Boost Spirit库.)
归档时间: |
|
查看次数: |
1185 次 |
最近记录: |