Подтвердить что ты не робот

Можно ли полностью отключить новый оператор С++ по умолчанию?

Поскольку наше приложение имеет жесткую производительность и ограничения памяти, наши стандарты кодирования запрещают использование кучи по умолчанию и— т.е. malloc, по умолчанию new. Каждое распределение памяти должно выбирать один из нескольких конкретных распределителей; что-то вроде

// declared globally
void* operator new( size_t size, CustomAllocHeap* heap, const char* perpetrator_name )
{
  return heap->Allocate( size, perpetrator_name );
} 
// imagine a bunch of CustomAllocHeap declared globally or statically, thus

Vector* v = new( gPhysicsHeap, __FUNCTION__ ) Vector( 1.0f, 2.0f, 3.0f, 4.0f );
// or in a class
Thingy* p = new( this->LocalArenaHeap, __FUNCTION__ ) Thingy();

Несмотря на то, что мы сохранили хорошую дисциплину в этом коде, некоторые стандартные компоненты С++ (контейнеры, std::function) скрытно обрабатывают кучу по умолчанию new, что очень плохо.

Было бы неплохо отключить значение по умолчанию new в некотором роде, так что любая строка кода, которая неявно приводит к распределению по умолчанию, сразу бросает ошибку компилятора. Это позволило бы сразу заметить эти вещи.

Очевидно, мы можем сделать new ошибку выполнения, т.е.

void* operator new ( size_t ) { __debugbreak(); return NULL; }  

но было бы гораздо лучше получить предупреждения об этом во время компиляции. Возможно ли это?

Наше приложение создано для фиксированной платформы (x64 с Visual Studio); переносимость не имеет значения.

4b9b3361

Ответ 1

Вы можете реализовать по умолчанию new для вызова нереализованной функции. Затем во время соединения вы получите сообщение об ошибке пользователя new:

#include <stdexcept>
inline void * operator new (std::size_t) throw(std::bad_alloc) {
    extern void *bare_new_erroneously_called();
    return bare_new_erroneously_called();
}

Когда я протестировал его на IDEONE, я получил эту ошибку:

/home/geXgjE/ccrEKfzG.o: In function `main':
prog.cpp:(.text.startup+0xa): undefined reference to `bare_new_erroneously_called()'
collect2: error: ld returned 1 exit status

В моих тестах, используя g++, нет никакой ошибки ссылки, если в программе нет ссылок на голый new. Это связано с тем, что g++ не испускает код для неиспользуемых функций inline.

У меня нет Visual Studio, установленной в моей системе, поэтому следующая информация основана только на некоторой документации, которую я нашел. Чтобы заставить оператора inline new видеть всюду, вы должны поместить его определение в файл заголовка, а затем использовать /FI detect_bare_new.h в вашем компиляторе. * В соответствии с этим ответом Visual Studio не будет генерировать код для неиспользуемых функций inline (например, g++). Однако вы должны проверить, есть ли уровень оптимизации, который должен быть включен для этого поведения или нет.

* g++ имеет аналогичную возможность компиляции: -include detect_bare_new.h.

Предполагается, что вы намерены передать свои собственные распределители шаблонам и классам С++ в стандартной библиотеке С++. Если вы этого не сделаете, то встроенный код в стандартных заголовках, которые вызывают распределитель по умолчанию (который вызовет new), также вызовет ошибку связывания. Если вы хотите, чтобы стандартная библиотека С++ использовала по умолчанию new, тогда простой способ заставить ее работать (за счет более длительных времени компиляции) - добавить все стандартные заголовки С++, которые вы планируете включить в начало файл detect_bare_new.h.

Вы заявляете, что переносимость решения для вас не важна. Но, ради полноты, я должен подчеркнуть, что Бен Фойгт правильно указывает: стандарт С++ не гарантирует поведение не генерирующего кода для неиспользуемых функций inline. Таким образом, можно получить ошибку связывания, даже если функция не используется. Но, если код не имеет других ссылок на нереализованную функцию, кроме как в пределах реализованной реализации new, ошибка будет находиться в самом определении new. Например, g++ может генерировать ошибку, например:

/home/QixX3R/cczri4AW.o: In function `operator new(unsigned int)':
prog.cpp:(.text+0x1): undefined reference to `bare_new_erroneously_called()'
collect2: error: ld returned 1 exit status

Если ваша система является той, которая генерирует код для неиспользуемых функций inline, у вас может быть временное решение. Обходной путь будет работать, если компоновщик сообщит все ошибочные ссылки на функцию undefined. В этом случае, если наблюдаемая единственная ошибка связывания связана с определением самого оператора new, неожиданных вызовов голых new нет. После проверки того, что код имеет только одну ошибку, вы могли бы изменить линию ссылок, чтобы включить объект или библиотеку, которая имеет соответствующее определение bare_new_erroneously_called(), которое приведет к исключению среды выполнения.

Ответ 2

Если ваш собственный "новый" оператор не назван "новым", но по-другому (например, "myNew" ), вы можете использовать "#define" таким образом, чтобы заменить "новый" мусором:

#define new *+-/&

Предкомпилятор теперь заменит "новый" :

x = new mytype;

От мусора:

x = *+-/& mytype;

Преимущество по сравнению с сообщением во время связывания заключается в том, что это немедленно генерирует сообщение компилятора при компиляции файла С++, а не в конце при связывании. Вы также видите линию, в которой находится "новое".

Недостатком является то, что вам придется "#include" файл, содержащий этот "#define" во всех файлах на С++ в вашем проекте.

Ответ 3

Отравить это!
Если вы используете GCC, для этого есть прагма:

#ifdef __GNUC__

/* poision memory functions */
#   pragma GCC poison malloc new

#endif