Как использовать std::nothrow и std::new_handler в стандартном заголовочном файле <new>
Наткнулся на небольшой стандартный заголовочный файл <new>
, Я, вероятно, не видел его прямого использования раньше. Вот версия g++ для тех, кому интересно.
Ниже часть моего интереса:
struct nothrow_t { };
extern const nothrow_t nothrow;
/** If you write your own error handler to be called by @c new, it must
* be of this type. */
typedef void (*new_handler)();
/// Takes a replacement handler as the argument, returns the previous handler.
new_handler set_new_handler(new_handler) throw();
- Как
struct nothrow_t
и его объектnothrow
используются программистами? Действительно ли объект должен бытьextern
? - Когда делает
new_handler
используемый? - Почему все
operator new/delete
объявлены вextern C++
блок?
2 ответа
nothrow_t
используется, чтобы сказать operator new
работать в обратно совместимом режиме "вернуть ноль при ошибке, а не выдавать исключение".
То есть, если вы видите такой код:
int * idx = new(std::nothrow) int;
это было бы nothrow_t
на работе. Для соответствующих разделов в стандарте начните с (по состоянию на C++11 N3376) 17.6.4.6 [replace.functions]/1 и продвигайтесь вниз оттуда.
Чтобы ответить на ваши конкретные вопросы:
Да, он действительно должен быть внешним, по крайней мере, в соответствии с 18.6 [support.dynamic]/1, который включает в себя:
namespace std { class bad_alloc; class bad_array_new_length; struct nothrow_t {}; extern const nothrow_t nothrow; typedef void (*new_handler)(); new_handler get_new_handler() noexcept; new_handler set_new_handler(new_handler new_p) noexcept; }
Кроме того, в 17.6.2.3 [using.linkage]/1 сказано, что "объекты в стандартной библиотеке C++ имеют внешнюю связь (3.5)". Функции и классы (например,
get_new_handler
а такжеset_new_handler
выше) явно не нужно аннотировать, чтобы иметь внешнюю связь, потому что они имеют внешнюю связь по умолчанию.new_handler
используется, когда пользователь переопределяет значение по умолчаниюoperator new
используется по телефонуset_new_handler
, Это просто тип указателя на функцию.- Вероятно, потому что подписи для
operator new
не зарезервированы в C.extern "C++"
сообщает компилятору, что им разрешено выполнять манипулирование именами и другие специфические для C++ функции для этих функций. Таким образом, вы можете скомпилировать один модуль перевода как C, а другой как C++, и связать их вместе в одном двоичном файле, не беспокоясь о том, что кто-то в C определил функцию, конфликтующую с компилятором.operator new
,
Ну, это действительно вопрос "прочитай документацию, пожалуйста". Любая хорошая вводная книга по C++ должна обсуждаться nothrow
, Например, "язык программирования C++" Бьярне, как я помню, делает.
Но в любом случае вы используете nothrow
превратить std::bad_alloc
исключения в результатах нулевого указателя, и вы используете новый обработчик для повторной попытки неудачного размещения.
На практике не забудьте поставить ::
перед new
когда вы используете nothrow
и вообще при использовании глобального размещения новых, чтобы избежать подбора новых мест размещения из классов. За исключением того, что в целом следует избегать размещения новых (как это и есть очень низкоуровневая функция языка), я бы сделал это, даже если это технически не имеет смысла. Так же, как хорошая привычка.
Пример:
#include <iostream> // std::wcout, std::endl
#include <stdlib.h> // EXIT_FAILURE, EXIT_SUCCESS
#include <new> // std::nothrow
using namespace std;
int main()
{
int* const p = ::new( std::nothrow ) int[0x7fffffff/sizeof(int)];
if( !p )
{
cout << "Allocation failed!" << endl;
return EXIT_FAILURE;
}
cout << "Alles success!" << endl;
delete[] p;
return EXIT_SUCCESS;
}
Вывод на мою систему:
[D: \ DEV \ тест] > а Распределение не удалось! [D:\ DEV \ тест] > _
Обратите внимание, что вышеприведенное предполагает 32-битный процесс:)