Рассмотрим этот макет моей ситуации.
во внешнем заголовке:
class ThirdPartyObject
{
...
}
мой код: (распространяется среди нескольких заголовков и исходных файлов)
class ThirdPartyObjectWrapper
{
private:
ThirdPartyObject myObject;
}
class Owner
{
public:
Owner() {}
void initialize();
private:
ThirdPartyObjectWrapper myWrappedObject;
};
void Owner::initialize()
{
//not weird:
//ThirdPartyObjectWrapper testWrappedObject;
//weird:
//ThirdPartyObject testObject;
}
ThirdPartyObject — это, естественно, объект, определенный сторонней (статически прекомпилированной) библиотекой, которую я использую. ThirdPartyObjectWrapper — это удобный класс, который устраняет необходимость в работе с ThirdPartyObject. Owner :: initialize () вызывается в ближайшее время после экземпляр Владелец создан.
Обратите внимание на две строки, которые я пометил как «странные» и «не странные» в Owner :: initialize (). Все, что я делаю здесь, это создание пары объектов в стеке с их конструкторами по умолчанию. Я ничего не делаю с этими объектами, и они разрушаются, когда покидают область видимости. Нет ошибок компоновки или компоновщика, я могу раскомментировать одну или обе строки, и код будет построен.
Однако, если я раскомментирую «странный», то получаю ошибку сегментации, и (вот почему я говорю, что это странно), это находится в совершенно не связанном месте. Не в конструкторе testObject, как вы могли ожидать, но в конструкторе Owner :: myObjectWrapper :: myObject. Странная строка даже никогда не вызывается, но каким-то образом ее наличие или отсутствие постоянно изменяет поведение несвязанной функции в статической библиотеке.
И учтите, что если я только раскомментирую «не странно», то он будет работать нормально, выполняя конструктор ThirdPartyObject дважды без проблем.
Я работаю с C ++ в течение года, поэтому меня не удивляет, что что-то подобное может произойти, но я почти достиг предела своей способности разобраться как это происходит. Мне нужен вклад людей со значительно большим опытом C ++, чем у меня.
Каковы некоторые возможности, которые могут вызвать это? Что здесь может происходить?
Кроме того, обратите внимание, я не прошу совета о том, как избавиться от segfault. Segfaults, я понимаю, я подозреваю, что это простое состояние гонки. Чего я не понимаю, так это поведения, так что это единственное, за что я пытаюсь получить ответы.
Мой лучший вывод в том, что это связано с заголовками и макросами. Сторонняя библиотека на самом деле уже имеет несколько ошибок, связанных с ее заголовками и макросами, например, код не будет собираться, если вы поместите # include в неправильном порядке. Я не изменяю никакие # include так строго, это все равно не имело бы смысла, но, возможно, компилятор оптимизирует включения на основе присутствия здесь символа? (это будет единственное упоминание ThirdPartyObject в файле)
Мне также приходит в голову, что, поскольку я использую Qt, возможно, в этом может быть задействован мета-объектный компилятор (который генерирует дополнительный код между компиляциями). Очень маловероятно, так как Qt не знает о сторонней библиотеке, в которой происходит segfault, и это на самом деле не имеет отношения к функциональности MOC (так как ThirdPartyObject ни в коем случае не передается в качестве аргумента), но его стоит изучить, по крайней мере, ,
Смежные вопросы предполагают, что это может быть относительно небольшое переполнение буфера или состояние гонки, которое вызывается оптимизацией компилятора. Продолжаем расследование, но все предложения приветствуются.
Типичные виновники:
Некоторые продукты сборки устарели и не совместимы с двоичными файлами.
У вас есть ошибка памяти, которая повредила состояние вашего процесса, и вы видите проявление этого в совершенно не связанном месте.
Исправление # 1 тривиально: удалите папку сборки и соберите снова. Если вы не встраиваете в папку теневых сборок, вы настроили себя на неудачу, надеюсь, теперь вы знаете достаточно, чтобы остановиться 🙂
Исправление № 2 не тривиально. Просмотреть ручное управление памятью и возможные переполнения буфера с подозрением. Используйте современные методы программирования C ++, чтобы использовать компилятор, чтобы помочь вам: хранить вещи по значению, использовать контейнеры, использовать умные указатели и использовать итераторы и range-for вместо указателей. Не используйте массивы в стиле C Abhor C-стиль API-интерфейсы (Type * array, int count)
вид — они не принадлежат C ++.
Как весело. Я сварил это до дна.
//#include <otherthirdpartyheader.h>
#include <thirdpartyobject.h>
int main(...)
{
ThirdPartyObject test;
return 0;
}
Этот код работает. Если я раскомментирую первое включение, удаляю все артефакты сборки и строю заново, то оно ломается. Очевидно, есть компонент заголовка / макроса и, возможно, какой-то компонент оптимизации компилятора. Но получите это, в соответствии с документацией библиотеки, это должно давать мне ошибку каждый раз, потому что я не выполнял требуемый шаг инициализации. Так что тот факт, что он работает вообще, указывает на неожиданное поведение.
Я объясняю это проблемами, связанными с библиотекой, а не проблемами широкого спектра C ++. Я свяжусь с продавцом в будущем, но спасибо всем за помощь.