Если Вы производите как не-Unicode (который Вы, по-видимому, делаете), необходимо просмотреть текстовый файл, Вы создаете использование того же кодирования использования Консоли. Вот почему это выглядит корректным в консоли. В некоторых текстовых редакторах можно выбрать кодирование (или "кодовая страница") при открытии файла. (Как произвести как Unicode, который я не знаю. cmd/U не делает то, что говорит документация.)
Консольное кодирование зависит от Вашей установки Windows. Для меня это - "западноевропеец (DOS)" (или просто "MS-DOS") в Microsoft Word.
Ответ на ваш первый вопрос: Вызывается виртуальный деструктор в вашей dll - информация о его местонахождении встроена в ваш объект (в vtable). В случае освобождения памяти это зависит от того, насколько дисциплинированы пользователи вашего IBase
. Если они знают, что им нужно вызвать Release ()
и считают, что исключение может обойти поток управления в неожиданном направлении, будет использовано правильное направление.
Но если CreateInterface ()
] возвращает shared_ptr
, он может связать правильную функцию освобождения прямо с этим интеллектуальным указателем. Ваша библиотека может выглядеть так:
Destroy(IBase* p) {
... // whatever is needed to delete your object in the right way
}
boost::shared_ptr<IBase> CreateInterface() {
IBase *p = new MyConcreteBase(...);
...
return shared_ptr<IBase>(p, Destroy); // bind Destroy() to the shared_ptr
} // which is called instead of a plain
// delete
Таким образом, каждый пользователь вашей DLL легко предотвращается от утечки ресурсов. Им никогда не придется беспокоиться о вызове Release ()
или обращать внимание на исключения, неожиданно обходя их поток управления.
Чтобы ответить на ваш второй вопрос: Обратная сторона этого подхода четко указана другим answer s: Ваша аудитория должна использовать тот же компилятор, компоновщик, настройки, библиотеки, что и вы. И если их может быть довольно много, это может стать серьезным недостатком для вашей библиотеки. Вы должны выбрать: безопасность или большая аудитория
Но есть возможная лазейка: Используйте shared_ptr
в своем приложении, т.е.
{
shared_ptr<IBase> p(CreateInterface(), DestroyFromLibrary);
...
func();
...
}
Таким образом, конкретный объект реализации не передается через Граница DLL. Тем не менее ваш указатель надежно спрятан за shared_ptr
, который '
Я бы не советовал использовать shared_ptr
в интерфейсе. Даже использование C ++ вообще в интерфейсе DLL (в отличие от подпрограмм, использующих только «extern C») проблематично, потому что изменение имен не позволит вам использовать DLL с другим компилятором. Использование shared_ptr
особенно проблематично, поскольку, как вы уже определили, нет гарантии, что клиент DLL будет использовать ту же реализацию shared_ptr
, что и вызывающий. (Это потому, что shared_ptr
является классом шаблона, а его реализация полностью содержится в файле заголовка.)
Чтобы ответить на ваши конкретные вопросы:
Я не совсем понимаю, о чем вы здесь спрашиваете. ... я m предполагая, что ваша DLL будет содержать реализации классов, производных от IBase
. Код их деструкторов (а также остальной код) в обоих случаях будет содержаться в DLL. Однако, если клиент инициирует уничтожение объекта (вызывая delete
в первом случае или позволяя последнему экземпляру shared_ptr
выйти за пределы области действия во втором случае), тогда деструктор будет вызываться из клиентского кода.
Изменение имени обычно предотвращает использование вашей DLL с другим компилятором ... но реализация shared_ptr
может изменить даже в новой версии того же компилятора, и это может вызвать у вас проблемы. Я бы не стал использовать второй вариант.
shared_ptr
гарантирует, что функция освобождения ресурсов будет вызываться в DLL. Выходом из этой проблемы является создание чистого интерфейса C и тонкой полностью встроенной оболочки C ++ вокруг него.
По вашему первому вопросу: я делаю обоснованное предположение и не говорю из опыта, но мне кажется, что во втором случае освобождение памяти будет вызываться «в .exe». Когда вы вызываете удалить объект,
происходят две вещи: во-первых, вызываются деструкторы, а во-вторых, освобождается память для объекта. Первая часть, вызов деструктора, определенно будет работать так, как вы ожидаете, вызывая правильные деструкторы в вашей dll. Однако, поскольку shared_ptr является шаблоном класса, его деструктор создается в вашем .exe, и поэтому он будет вызывать оператор delete () в вашем exe, а не в .dll. Если они были связаны с разными версиями среды выполнения (или даже статически связаны с одной и той же версией среды выполнения), это должно привести к ужасному неопределенному поведению (это та часть, в которой я не совсем уверен, но это кажется логичным) . Есть простой способ проверить, правда ли то, что я сказал - переопределить глобальный оператор delete в вашем exe, но не в вашей dll, поставить в нем точку останова и посмотреть, что вызывается во втором случае (я бы сам делаю это, но у меня, к сожалению, столько времени, чтобы расслабиться).
Обратите внимание, что те же ошибки существуют и для первого случая (вы, кажется, это понимаете, но на всякий случай). Если вы сделаете это в exe:
IBase *p = CreateInterface();
delete p;
, то вы попали в одну и ту же ловушку - вызов оператора new в dll и вызов оператора delete в exe. Вы'