VS2015新加入的编译选项/Zc:throwingNew

[原文发表地址] New in VS 2015: /Zc:throwingNew

[原文发表时间] 2015/08/06 7:05AM

C++标准定义了操作符new分配内存失败时不会返回null值, 只会抛出异常. 在近期发布的VS产品的VC++编译器中的操作符new分配内存失败后也是同标准定义一样抛出异。

但是,编译器的这种行为并不是一直是这样的,追溯到VS6.0, 编译器在new操作内存分配失败时却只会返回null值。 操作失败抛出异常标准的行为直到VS2002才开始沿用, 但是较老版本操作失败返回null值的行为仍然保留,可以通过提供的nothrownew.obj机制达到这一目的。

这对现在的编译器有什么影响?从VS2002起, 编译器防止潜在的操作符new会返回空值的情况, 因此,如果你在VS2013写出了下面的代码:

void foo() {
A* a = new A();
// ...
}

理论上编译器会生成这样的代码:

void foo() {
A* a = malloc(sizeof(A));
if (a) {
new(a) A();
}
// ...
}

也就是说,这里会检查分配内存操作返回null值的情况, 反之, 会在已经分配的内存上进行运行构造操作。

null值检查的缺陷之处是, 它们膨胀了代码量,同时也抑制了一些有用的编译器优化行为,(如:虚拟化,使用常量传播对象替代通用初始化对象). 编译器会花费很多精力去生成这些不必要的代码段来尽力支持使用nothrownew.obj机制或者客户定制的new操作行为。

VS2015中, 我很欣慰的说我们针对这个问题的纠正已经迈出了第一步, 使用/Zc:throwingNew. 当你在编译命令行指定了/Zc:throwingNew开关时, 它会通知编译器假定程序将会链接到一个标准的操作符new行为,这个标准行为将会忽略检查null值带来的额外开销。

最好的是, 与链接选项LTCG一起编译时, 编译器会检测是否使用默认的操作符new实现方法,即使不适用/Zc:throwingNew开关,也会自动忽略检查null值。

/Zc:throwingNew 可能会成为未来编译器的默认行为, 目前我还是推荐大家指定/Zc:throwingNew开关(尤其是不使用LTCG链接开关的情况),如果你使用默认操作符new行为, 你只需与用户的分配操作行为保持一致。 指定/Zc:throwingNew开关可以通过点击项目中的属性页面,从配置下拉框中选择所有配置,打开配置属性页面,C/C++, 命令行(Command line),在附加编译选项中添加/Zc:throwingNew, 点击Ok。