两个简单的问题:在普通C
中,我们经常使用xmalloc
这是一个分配或中止例程。我用C ++实现了它。这是一个正确的无异常实现吗?
template <typename T>
T *xnew(const size_t n)
{
T *p = new (std::nothrow) T[n];
if (p == nullptr)
{
cerr << "Not enough memory\n";
abort();
}
return p;
}
int main()
{
int *p = xnew<int>(5000000000LL);
}
第二个问题,如果我从<int>
调用中删除xnew<int>(5000000000LL);
,编译器(g ++ 4.7.2)就不能再推断那个[T = int]
了,尽管返回类型int *
仍然存在。那是为什么?
编辑:使用new
版本时,是否有任何开销,即使它没有被抛出也会引发异常?在非绝对必要时,我真的不想使用任何例外。
答案 0 :(得分:5)
我不明白为什么这是必要的。 new
会抛出std::bad_alloc
如果它没有分配内存。如果你不处理异常,这个
将导致致电std::terminate
,这有效地结束了
程序,与xmalloc
具有相同的行为。
当然,当编译器没有实现异常时,这会发生变化。
答案 1 :(得分:3)
第二个问题,如果我从中移除
<int>
xnew<int>(5000000000LL);
调用,编译器(g ++ 4.7.2)无法推断 虽然返回类型int *仍然存在,但[T = int]已经存在。为什么 那是吗?
函数模板参数仅从函数调用中的参数表达式的类型推导出来。由于T
没有以任何方式出现在函数参数中,因此无法推断出它。
使用函数调用的返回值所做的事情不会影响C ++中的模板参数推导。如果您编写int *p = some_function(5000000000LL);
,那么int*
不一定是返回类型some_function
,它是编译器将尝试转换{的返回类型的类型{1}}。
因此,编译器无法推导some_function
的近因是标准禁止它(至少没有诊断)。最终的原因是C ++的设计者(最初可能是Stroustrup)想要限制所考虑的事情
对于扣除,保持规则,如果不简单,那么至少可以理解为凡人的头脑。
C ++中有一条规则,即子表达式的类型仅取决于子表达式本身,而不取决于周围的表达式。而AFAIK只有一个例外,即函数指针或成员函数指针不明确时:
int
答案 2 :(得分:1)
此代码不保证100%安全,因为operator<<()
可能会抛出。实际上它并不普遍,因为扔掉那里会遇到一些罕见的情况:
std::cerr
在其badbit
掩码中设置exceptions()
(默认情况下不是)在这种情况下,异常将被重新抛出并且内存将泄漏。
关于从模板函数调用表达式中删除<int>
- 当然它不起作用。编译器只能从调用表达式本身推导出模板参数类型,而不是从它将被赋值的左值类型推导出。因此,您希望自动推导出的模板参数应该是函数参数,而不是返回类型:
template <class T> T f1();
template <class T> T f2(T);
int a = f1(); // Will not compile, shall be f1<int>();
int b = f2(42); // OK
异常开销实际上取决于实现。我相信现代编译器足够聪明,如果可能的话可以避免这种开销,但是你应该用你的平台来检查它。
答案 3 :(得分:1)
如果你想避免抛出new
的异常(无论出于何种原因 - 也许你正在一个不支持例外的平台上工作,比如某些嵌入式平台),你可以提供{{1}如果new_handler
无法分配内存,则中止程序:
new
仅仅将此源文件作为程序的一部分包含将安装#include <stdlib.h>
#include <iostream>
#include <new>
namespace {
void new_handler_abort()
{
std::cerr << "Not enough memory\n";
abort();
}
struct new_handler_abort_installer {
new_handler_abort_installer() {
std::set_new_handler(new_handler_abort);
}
};
// a statically allocated object that does nothing but install the
// new_handler_abort() function as the new_handler
new_handler_abort_installer install_new_handler_abort;
}
将导致程序中止,new_handler
无法分配内存。
然而:
new
被调用之前发生)。因此,如果您在main()
之前遇到内存问题,则可能无法完全按照您的意愿执行。main()
时发生的代码,因此可能仍会有一些少量的开销处理永远不会发生的异常(较新的编译器可以通过使用表驱动的堆栈展开来避免这种开销,避免必须运行代码来在每次调用时设置异常)。