C ++ смешивать новые / удалять между библиотеками? - PullRequest
13 голосов
/ 14 января 2009

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

Ответы [ 6 ]

16 голосов
/ 14 января 2009

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

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

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

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

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, который удаляет объект, когда его счетчик ссылок падает до нуля.

10 голосов
/ 14 января 2009

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

6 голосов
/ 14 января 2009

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

5 голосов
/ 14 января 2009

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

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

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

3 голосов
/ 15 января 2009

Старая новая вещь уже говорила об этом. Он также приводит список основных решений Microsoft.

1 голос
/ 14 января 2009

Вы совершенно правы, что там есть проблема, но в большинстве случаев есть даже более простое решение, чем предлагали другие ответы (пока что). Вы можете продолжать использовать new и свободно удалять - все, что вам нужно сделать, это перегрузить new и delete для каждого класса в вашей библиотеке, который может использоваться через границы 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» - new и delete могут нормально использоваться для них, не беспокоясь о границах DLL.

...