Новые и удаленные операторы переопределяются в библиотеках
Что произойдет, если две библиотеки (связанные динамически) имеют свою глобально переопределенную версию операторов new и delete, и они используют собственное управление памятью?
Как правило, неправильно отправлять средства управления памятью внутри библиотеки или может быть полезно в некоторых случаях обеспечить управление памятью только для некоторых определенных классов, определяя только класс новый и delete переопределяют?
Существуют ли какие-то различия в случае статических связанных библиотек?
Ответы
Ответ 1
В общем, это помечено как "здесь драконы". Это зависит от всех видов вещей. Часто две библиотеки будут сражаться, а новые и удаленные будут в конечном итоге переопределены одним из них - это лучшее, на что вы можете надеяться.
Альтернатива:
-
Запустится библиотека A. Переопределяет новый/удалять, выделяет некоторую память. Библиотека B запускается и переопределяет. При выключениях системы библиотека памяти A освобождается с удалением библиотеки B. Это нехорошо.
-
Память, выделенная в библиотеке A, использует библиотеку A override и аналогично для библиотеки B. Если вы когда-нибудь закончите с памятью, выделенной в библиотеке A, освобожденной библиотекой B, вы проиграете. (И это может быть еще более запутанным, потому что, если объект, удаленный B, имеет виртуальный деструктор, удаление может закончиться тем, что выполняется A... поэтому оно работает.)
Ответ 2
Я думаю, что Мартин довольно хорошо отвечает на ваш вопрос, излагая, что происходит, а также ничего, что это немного опасно и не очень желательно (там действительно есть драконы). Позвольте мне немного расширить его, предоставив альтернативу: избегайте переопределения new/delete и вместо этого используйте концепцию распределителя.
Например, если вы посмотрите на std::vector, вы заметите, что он настроен как на то, что он хранит, но также на распределитель. Написав соответствующий распределитель, вы можете точно определить, как std::vector выделяет и деблокирует память. Обратите внимание, насколько это хорошо и слабо связано: вы не испытываете затруднений при выполнении полного контроля над распределением памяти, даже если вы не можете изменить исходный код std::vector.
Если вы хотите, чтобы библиотека B выполняла выделение определенным образом, я бы сделал следующее:
- Напишите распределитель, который соответствует концепции распределителя так, как это делает std:: allocator.
- Убедитесь, что все классы, которые используют динамическое распределение памяти напрямую (например, не через одного из своих членов), знают о распределителе.
- Typedef все эти классы, чтобы по умолчанию использовать ваш распределитель.
Чтобы прояснить шаг 3, я имею в виду, что напишу что-то вроде этого в базовом файле заголовка вашей библиотеки:
template <class T>
using my_lib::vector = std::vector<T, my_lib::MyAllocator<T>>;
Теперь вы можете использовать '' vector '' в любом месте вашей библиотеки, что будет нормальным вектором, но с использованием вашей схемы размещения.
Шаг 1 может варьироваться от очень простого (для ассемблерных распределителей) до довольно сложного (там есть несколько gotcha с расщепленными с состоянием). Что касается шага 2, это довольно просто, поскольку динамическая память для контейнеров идет, поскольку все контейнеры в стандартной библиотеке уже поддерживают это. Но если вы используете динамическую память, например. полиморфизм, вам нужно сделать немного дополнительной работы (возможно, написать подходящую оболочку), чтобы сделать это с помощью средства, выделяемого распределителем.
Если у кого-то есть хорошие примеры причин, по которым вы хотите переопределить new/delete, а не использовать распределители (например, потому что что-то, что вы не можете сделать с распределителями), мне было бы интересно их услышать.
Изменить: и просто чтобы привести этот полный круг, обратите внимание, что при использовании библиотек A и B не будет проблем, если вы это сделаете, поскольку глобальные операторы не были переопределены.