Правильный аналог xmalloc в переполнении стека

Два простых вопроса: простым 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 версия, которая может выдать исключение, даже если оно не выброшено? Я действительно не хочу использовать какие-либо исключения, когда это не является абсолютно необходимым.

1

Решение

Я не понимаю, почему это необходимо. new будет бросать std::bad_alloc
если не удается выделить память. Если вы не обрабатываете исключения, это
приведет к звонку std::terminate который эффективно заканчивает
программа и имеет такое же поведение, как xmalloc,

Конечно, это меняется, когда ваш компилятор не реализует исключения.

5

Другие решения

Второй вопрос, если я уберу <int> от
xnew<int>(5000000000LL); вызов, компилятор (g ++ 4.7.2) не может сделать вывод, что
[T = int] больше, хотя возвращаемый тип int * все еще там. Зачем
в том, что?

Аргументы шаблона функции выводятся только из типов выражений аргумента в вызове функции. поскольку T никак не отображается в параметрах функции, не может быть выведено.

То, что вы делаете с возвращаемым значением вызова функции, никогда не влияет на вывод аргументов шаблона в C ++. Если ты пишешь int *p = some_function(5000000000LL); затем int* не является обязательно возвращаемый тип some_functionэто тип, в который компилятор будет пытаться преобразовать возвращаемый тип some_function,

Так что проксимальная причина, по которой компилятор не может вывести int в том, что стандарт это запрещает (по крайней мере, без диагностики). Конечная причина заключается в том, что дизайнеры C ++ (вероятно, изначально Страуструпа) хотели ограничить принятые во внимание вещи.
для дедукции — соблюдать правила, если не простые, то, по крайней мере, понятные для смертных умов.

В C ++ существует правило, согласно которому тип подвыражения зависит только от самого подвыражения, а не от окружающего выражения. И AFAIK есть только одно исключение, когда указатель на функцию или указатель на функцию-член является неоднозначным:

void foo();
void foo(int);

void (*pfoo1)() = &foo; // &foo evaluates to a pointer to the void overload
void (*pfoo2)(int) = &foo; // &foo evaluates to a pointer to the int overload
void (*pfoo3)() = (void(*)(int))&foo; // &foo evaluates to the int overload, but doesn't convert to the type of pfoo3 so the line fails.
3

Этот код не гарантирует 100% безопасность, потому что operator<<() мог бросить. Практически это не распространенный случай, потому что для броска должны быть соблюдены некоторые редкие условия:

  1. std::cerr имеет badbit установить в своем exceptions() маска (по умолчанию это не так)
  2. Исключение выбрасывается во время вывода

В этом случае исключение будет переброшено и утечка памяти.

Об удалении <int> из выражения вызова функции шаблона — конечно, это не будет работать. Компилятор может определить тип аргументов шаблона только из самого выражения вызова, а не из типа lvalue, которому он будет присвоен. Поэтому аргументы шаблона, которые вы хотите автоматически выводить, должны быть аргументами функции, а не возвращаемого типа:

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

Исключения накладных расходов на самом деле зависит от реализации. Я полагаю, что современные компиляторы достаточно умны, чтобы избежать таких накладных расходов, если это возможно, но вы должны проверить это с вашей платформой, чтобы быть уверенным.

1

Если вы хотите избежать исключения new (по любой причине — может быть, вы работаете на платформе, где исключения не поддерживаются, например на некоторых встроенных платформах), вы можете предоставить 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(), он может не делать именно то, что вы хотите.
  • компилятор может все еще добавить код для поддержки обработки исключений, а для некоторых компиляторов, который включает код, который происходит при каждом вызове operator new так что еще может быть немного небольшое количество накладных расходов, затрачиваемое на обработку исключений, которые никогда не возникнут (более новые компиляторы могут избежать этих накладных расходов, используя раскрутку стека, управляемого таблицей, что позволяет избежать необходимости запуска кода для установки исключений при каждом вызове).
1
По вопросам рекламы ammmcru@yandex.ru
Adblock
detector