С++ mix new/delete между libs?

Если я использую ключевое слово new в моей библиотеке (которое построено иначе, чем мое основное приложение), когда я удаляю его в своем основном приложении с помощью delete, есть ли вероятность, что я могу получить сообщение об ошибке/ошибке

Ответ 1

да действительно. В частности, вы обнаружите, что проблемы с отладкой/выпуском кучи различны, также если ваша библиотека использует новое место размещения или любую пользовательскую кучу, у вас возникнет проблема. Проблема Debug/Release на сегодняшний день является наиболее распространенной.

Ответ 2

Это зависит. Если вы говорите о статической библиотеке, то, вероятно, вы будете в порядке - код будет работать в том же контексте, что и основная программа, используя ту же библиотеку времени выполнения С++. Это означает, что new и delete будут использовать одну и ту же кучу.

Если вы говорите об общей библиотеке (DLL), то вы, вероятно, не будете в порядке. Код, запущенный в DLL, может использовать другую библиотеку времени выполнения С++, что означает, что компоновка кучи будет отличаться. DLL может вообще использовать другую кучу.

Вызов delete (в основной программе) указателя, выделенного DLL (или наоборот) приведет к (в лучшем случае) немедленному сбою или (в худшем случае) повреждению памяти, которое займет некоторое время для отслеживания вниз.

У вас есть несколько вариантов. Во-первых, используйте шаблон метода factory для создания и удаления этих объектов:

Foo *CreateFoo();
void DeleteFoo(Foo *p);

Они не должны быть реализованы в файле заголовка.

В качестве альтернативы вы можете определить метод Destroy для объекта:

class Foo
{
    ~Foo();

public:
    virtual void Destroy();
};

... снова, не реализуйте это в файле заголовка. Вы бы выполнили его так:

void Foo::Destroy()
{
    delete this;
    // don't do anything that accesses this object past this point.
}

Обратите внимание, что деструктор для Foo является закрытым, поэтому вам нужно вызвать Foo::Destroy.

Microsoft COM делает что-то подобное, где он определяет метод Release, который удаляет объект, когда его счетчик ссылок падает до нуля.

Ответ 3

Да, ты будешь. Простое решение - предоставить функции Create и Delete в вашей библиотеке, которые можно вызывать из основного приложения. Функция Create выполнит новое и вернет указатель, который затем будет передан в функцию Delete для удаления.

Ответ 4

Это проблема, которую я видел только в Windows.

В системах Unixish нет привычки заставлять разделяемые библиотеки ссылаться на разные версии одной и той же библиотеки в одной и той же программе, и все загруженные символы видны глобально. Это означает, что если объект выделяется в одной части кода и удаляется в другой, обе используют одну и ту же системную библиотеку для этого.

Я должен сказать, что эта проблема, создаваемая Windows с использованием различных библиотек времени выполнения C, действительно раздражает и неестественна для программиста на C. Посмотрите на библиотеку C; он имеет функции, такие как strdup, которые malloc string и ожидают, что программист вызовет на нем free(). Но сделайте то же самое в своей собственной библиотеке в Windows и просто дождитесь взрыва. Вам также придется ждать, потому что это не произойдет во время разработки, но только после того, как вы предоставили скомпилированную DLL для другого плохого сока.

Ответ 5

Old New Thing уже до этого догадался. Он также дает список основных решений Microsoft.

Ответ 6

Вы совершенно правы, что там есть проблема, но в большинстве случаев там было предложено еще более простое решение, чем другие ответы (до сих пор). Вы можете продолжать использовать новые и удалять свободно - все, что вам нужно сделать, это перегрузить новые и удалить для каждого класса в вашей библиотеке, которые могут использоваться через границы DLL.

Лично я просто определил простой класс, чтобы обеспечить необходимую функциональность:

class NewDelete
{
    public:
        void *operator new (size_t size);
        void operator delete (void *memory);
        void *operator new (size_t size, void *ptr);
        void operator delete (void *memory, void *ptr);
};

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