По причинам, по которым я не хочу вдаваться в подробности здесь, мне нужно полностью инструментировать распределения и освобождения в большой существующей кодовой базе, которая использует пользовательские operator new
и operator delete
. С этой целью я переписываю (используя проход внешнего интерфейса clang) каждое вхождение new X(...)
на new (allocate<X>()) X(...)
, где реализация allocate<X>
по умолчанию вызывает специфичный для класса operator new
, если он существует, или ::operator new
в противном случае. Я знаю, что это не стопроцентный звук, но он «достаточно хорош» для той базы кода, на которую я ориентируюсь.
Теперь я пытаюсь ввести аналогичное поведение перехвата для delete
, но это сложнее, чем ожидалось, из-за странной природы виртуального operator delete
. От https://en.cppreference.com/w/cpp/memory/new/operator_delete:
The call to the class-specific
T::operator delete
on a polymorphic class is the only case where a static member function is called through dynamic dispatch.
Рассмотрим следующий пример:
struct A {
virtual ~A() { cout << "~A" << endl; }
static void operator delete(void* ptr) {
cout << "A::operator delete" << endl;
::operator delete(ptr);
}
};
struct B : public A {
virtual ~B() { cout << "~B" << endl; }
static void operator delete(void* ptr) {
cout << "B::operator delete" << endl;
::operator delete(ptr);
}
};
В идеале я хотел бы заменить каждое вхождение delete x
в базе кода на deleteWrapper(x)
, который по умолчанию эквивалентен delete x
, но при необходимости я могу добавить дополнительные инструменты и специализировать его для определенных типов x
.
Я думал, что могу сделать что-то вроде следующего, чтобы добиться этого:
template <typename T>
void deleteWrapper(T* x) {
x->~T();
// The deallocation behavior here should "tweakable" for certain types
// of T, for example I might need different implementations for
// std::pair<int, int> and std::pair<std::string, int>
x->operator delete(x);
}
Но (помимо вызова неопределенного поведения) это не эквивалентно delete x
: с приведенной выше иерархией типов deleteWrapper(static_cast<A*>(new B))
вызывает A::operator delete
вместо B::operator delete
. Есть ли способ вызвать правильную перегрузку по полиморфному указателю без использования delete
?
Если есть решение, которое включает в себя определенную перезапись исходного кода, мне было бы интересно услышать об этом, так как я уже использую модифицированный clang.
Не было бы проще поместить инструментарий в специфичные для класса (и / или глобально замененные) версии operator new()
и operator delete()
, вместо того, чтобы пытаться редактировать каждое вхождение выражения new
или delete
? Редкая программа имеет более чем умеренное количество перегруженных / замененных версий этих функций, тогда как количество выражений new
и delete
может быть огромным.
@Peter WebKit имеет много классов с оператором new / delete, перегруженным для размещения объектов либо в куче с «быстрым размещением», либо даже в изолированных кучах, зависящих от типа. Иногда мне нужен другой специальный регистр оператора new / delete даже для разных экземпляров одного и того же шаблона, что потребует нетривиальных перезаписей. Я обсудил вариант использования с n.m, который был перемещен в чат.
Будет ли решение использовать ваш собственный класс интеллектуального указателя?