Не могли бы вы объяснить эту проблему удаления C ++?
-
23-09-2019 - |
Вопрос
У меня есть следующий код:
std::string F()
{
WideString ws = GetMyWideString();
std::string ret;
StringUtils::ConvertWideStringToUTF8(ws, ret);
return ret;
}
WideString-это сторонний класс, как и StringUtils. Они для меня черная коробка. Второй параметр передается ссылкой.
Когда я прохожу отладчик линию return ret
бросает неприятное всплывающее окно (Visual C ++), говоря, что куча может быть повреждена. При более тщательной копии возвращаемой строки в порядке, но удаление ret
терпит неудачу. ret
содержит правильное значение перед возвратом.
Что может сделать функция преобразования, чтобы вызвать это? Есть идеи для исправления?
Обновлять:
- Сам проект - это DLL
- StringUtils - это либера
- Проект составлен против многопоточного CRT (не отладка, а не DLL)
- Программа, кажется, работает нормально при запуске за пределами Visual Studio
Решение
- Если StringUtils была скомпилирована отдельно (например, с другой версией компилятора), у вас может быть конфликт в макете объекта.
- Если StringUtils находится в DLL, вы должны убедиться, что как ИТ, так и основная программа будут составлены для использования стандартной библиотеки в DLL. В противном случае каждый модуль (исполняемый файл и DLL) будет иметь свою кучу. Когда Stringutils пытается играть с данными в строке, которая была выделена из другой кучи, случаются плохие вещи.
Другие советы
Дизайнер StringUtils разработал очень плохой API. Ни один из шаблонных стандартных типов библиотеки не должен использоваться в публичном интерфейсе API. std::string
взорвано в линии. Таким образом, если используемые вами компилятор и библиотеки не совсем то же самое компилятор и библиотеки, используемые исполнителем StringUtils, типы могут и, вероятно, будут разными. По сути, реализатор StringUtils Не удалось отделить интерфейс от реализации.
Иллюстрация проблемы. Предположим, вы используете MSVC 9.0 SP1 и я использую MSVC 8.0. На моем компиляторе реализация Std :: String может выглядеть так:
class string
{
// : : stuff
private:
int someInt_;
char* someBuf_;
};
... но на вашем компиляторе это может выглядеть по -другому:
class string
{
// : : stuff
private:
void* impl_;
};
Если я напишу библиотечную функцию:
void DoSomethingWithAString(std::string& str);
... и вы называете это, sizeof(string)
в вашем коде будет отличаться от sizeof(string)
в моем коде. Типы не одинаковы.
У вас действительно есть только 2 решения вашей проблемы:
1) [Предпочтительнее] Получить реализатора StringUtils, чтобы исправить его разбитый код.
2) Замените библиотеку, используемую вашим компилятором в соответствии с библиотекой, используемой реализователем Stringutil. Возможно, вы сможете выполнить это, используя тот же компилятор на том же уровне патчей, что и использовался реализатор, предполагая, что он не заменил реализацию стандартной библиотеки.
РЕДАКТИРОВАТЬ: 3) Третий вариант - прекратить использование StringUtils. Честно говоря, это, вероятно, то, что я сделаю.
Из того, какой маленький код вы показываете, я полагаю StringUtils::ConvertWideStringToUTF8()
принимает std::string&
как второй параметр. Учитывая это, я не вижу, как ваш код может вызвать коррупцию кучи.
Обратите внимание, однако, что связывание библиотек C ++ в целом работает только тогда, когда код был составлен с использованием того же компилятора и одних и тех же настройки компилятора.
Ваше использование StringUtils
а также WideString
Делает это так, как будто вы используете C ++ Builder. Вы пытаетесь смешать модуль C ++ Builder и визуальный модуль C ++? Если это так, то вы определенно увидите проблемы, которые вы описали.
Вы не можете передать визуальный C ++ std::string
к функции C ++ Builder, потому что код застройщика C ++ будет предполагать, что параметр использует C ++ Builder std::string
определение. Классы могут иметь разные поля, а общие поля могут быть в другом порядке.
Даже если классы имеют одинаковые определения, модули все равно будут использовать разные менеджеры по памяти. Функция COLLED выделяет память для нового содержимого строки, используя свой диспетчер памяти, и вызывающий абонент будет использовать свой собственный диспетчер памяти, чтобы попытаться освободить содержимое строки позже.