LINUX.ORG.RU

Переделал класс на статические члены - получил сегфолт

 , ,


0

2

Проблема в следующем.

Был у меня класс EditorToolBar, и были у него все члены динамическими. А люди на ЛОРе говорят: негоже динамические члены держать, коль они полюбому используются в одном экземпляре. Ты их статическими сделай, чтоб хорошо было. Послушался я людей с ЛОРа, и переписал класс. И не стало в нем никаких указателей. Не столо в нем никаких динамических выделений памяти. Простой получился класс. А класс большой был. Скомпилировал я программу, запустил, посмотрел краем глаза как работает: работает как надо. И сделал коммит:

https://github.com/xintrea/mytetra_dev/commit/f979fa1721b2d66d18a501d914c86c1...

Да не углядел я, что бага змиёй подколодной где-то в коде затаилась, и стал дальше прогу писать. Вдруг человек прохожий говорит: это что ж у тебя программа падать начала на ровном месте?

https://github.com/xintrea/mytetra_dev/issues/22

Глянул я, ах ты ж ё-маё, как же не доглядел то? Трабла такая, что прогу юзать непозволительно. Стал я багу искать.

У класса EditorToolBar наследник есть, а зовут его EditorToolBarAssistant. И наследник этот тоже простой как угол дома: нет у него никаких динамических членов, только два указателя - на редактор, да на область редактирования. Этот наследник и вставляется в редактор завместно EditorToolBar.

Когда юзер запись добавляет, ему редактор показывается. Редактор Editor зовется. А как юзер окей-то нажмет, так сегфолт и выпрыгивает. И книжка волшебная такие строки кажет:

0	__kernel_vsyscall			0xb7774424	
1	__GI_raise	/usr/lib/debug/lib/i386-linux-gnu/i686/cmov/libc-2.19.so	56	0xb5f2a307	
2	__GI_abort	/usr/lib/debug/lib/i386-linux-gnu/i686/cmov/libc-2.19.so	89	0xb5f2b9c3	
3	__libc_message	/usr/lib/debug/lib/i386-linux-gnu/i686/cmov/libc-2.19.so	175	0xb5f686f8	
4	malloc_printerr	/usr/lib/debug/lib/i386-linux-gnu/i686/cmov/libc-2.19.so	4996	0xb5f6e76a	
5	_int_free	/usr/lib/debug/lib/i386-linux-gnu/i686/cmov/libc-2.19.so	3840	0xb5f6f3bd	
6	operator delete(void *)			0xb6152e68	
7	QFontComboBox::~QFontComboBox()			0xb728deaa	
8	QWidgetAction::~QWidgetAction()			0xb716c22e	
9	QWidgetAction::~QWidgetAction()			0xb716c272	
10	QObjectPrivate::deleteChildren()			0xb667343c	
11	QWidget::~QWidget()			0xb716a8fc	
12	QToolBar::~QToolBar()			0xb731aa7f	
13 !!!	EditorToolBar::~EditorToolBar	EditorToolBar.cpp	14	0x812084d	
14	EditorToolBarAssistant::~EditorToolBarAssistant	EditorToolBarAssistant.cpp	66	0x812f280	
15	EditorToolBarAssistant::~EditorToolBarAssistant	EditorToolBarAssistant.cpp	69	0x812f2c7	
16	QObjectPrivate::deleteChildren()			0xb667343c	
17	QWidget::~QWidget()			0xb716a8fc	
18	Editor::~Editor	Editor.cpp	51	0x807f3c4	
19	AddNewRecord::~AddNewRecord	AddNewRecord.cpp	38	0x80c2067	
20	RecordTableController::addNewRecord	RecordTableController.cpp	557	0x811134a	
21	RecordTableController::addNewToEndContext	RecordTableController.cpp	521	0x8110d39	
22	RecordTableController::qt_static_metacall	moc_RecordTableController.cpp	128	0x815eed0	
23	QMetaObject::activate(QObject *, int, int, void * *)			0xb6674a61	
24	QMetaObject::activate(QObject *, QMetaObject const *, int, void * *)			0xb667568b	
25	QAction::triggered(bool)			0xb711244d	
26	QAction::activate(QAction::ActionEvent)			0xb71144e8	
27	QToolButton::nextCheckState()			0xb7324390	
28	??			0xb724181e	
29	QAbstractButton::mouseReleaseEvent(QMouseEvent *)			0xb7241b0e	
30	QToolButton::mouseReleaseEvent(QMouseEvent *)			0xb732442d	
31	QWidget::event(QEvent *)			0xb716339d	
32	QAbstractButton::event(QEvent *)			0xb7240e59	
33	QToolButton::event(QEvent *)			0xb7325683	
34	QApplicationPrivate::notify_helper(QObject *, QEvent *)			0xb71204c4	
35	QApplication::notify(QObject *, QEvent *)			0xb7123db4	
36	QCoreApplication::notifyInternal(QObject *, QEvent *)			0xb663e88e	
37	QApplicationPrivate::sendMouseEvent(QWidget *, QMouseEvent *, QWidget *, QWidget *, QWidget * *, QPointer<QWidget>&, bool)			0xb7122c18	
38	??			0xb7180a8e	
39	??			0xb7183919	
40	QApplicationPrivate::notify_helper(QObject *, QEvent *)			0xb71204c4	
41	QApplication::notify(QObject *, QEvent *)			0xb7124047	
42	QCoreApplication::notifyInternal(QObject *, QEvent *)			0xb663e88e	
43	QGuiApplicationPrivate::processMouseEvent(QWindowSystemInterfacePrivate::MouseEvent *)			0xb6b10864	
44	QGuiApplicationPrivate::processWindowSystemEvent(QWindowSystemInterfacePrivate::WindowSystemEvent *)			0xb6b140e0	
45	QWindowSystemInterface::sendWindowSystemEvents(QFlags<QEventLoop::ProcessEventsFlag>)			0xb6af4f06	
46	??			0xb1b3f651	
47	g_main_context_dispatch			0xb5dbcda4	
48	??			0xb5dbd0c9	
49	g_main_context_iteration			0xb5dbd196	
50	QEventDispatcherGlib::processEvents(QFlags<QEventLoop::ProcessEventsFlag>)			0xb66a0258	
51	??			0xb1b3f8a6	
52	QEventLoop::processEvents(QFlags<QEventLoop::ProcessEventsFlag>)			0xb663c2c6	
53	QEventLoop::exec(QFlags<QEventLoop::ProcessEventsFlag>)			0xb663c73c	
54	QCoreApplication::exec()			0xb6641ef9	
55	QGuiApplication::exec()			0xb6b0a0a4	
56	QApplication::exec()			0xb7119754	
57	main	main.cpp	922	0x806d832	


Стал я по стеку вызовов прыгать, да место заветное искать, где бага поселилась. И так искал, и эдак искал, три дня и три ночи позади, да так и не нашел.

Сижу теперь, голову понурил, а как найти багу не знаю...

Тридевятое царство тут: https://github.com/xintrea/mytetra_dev/commits/experimental

Актуальный коммит, где бага живет: dee54f3

★★★★★

Динамический член. Это как?

pon4ik ★★★★★
()

С терминологией у вас тебя мил человек.

А бага, в том, что два раза объект ты удаляешь.

pon4ik ★★★★★
()

QWidget::~QWidget()

Destroys the widget.

All this widget's children are deleted first. The application exits if this widget is the main widget.

Вызывая setParent, ты указываешь виджету удалять всех детей при его удалении. Но так как дети не динамически выделены, ты пытаешься удалять то, что не выделял.

Совет тебе дали верный, но для нормального Си++. Но Си++ с Qt, да и с Gtkmm, странный. Ради борьбы с утечками авторы тулкитов ввели свои механизмы управления памятью, которые идут вразрез с обычным подходом в Си++.

i-rinat ★★★★★
()
Ответ на: комментарий от i-rinat

То есть получается, что в Qt нельзя статическому члену класса задавать parent на объект своего класса, который для него и является родительским???

Потому что в этом случае мы всегда получим сегфолт при удалении объекта, ибо будут пытаться удаляться статические члены??

Xintrea ★★★★★
() автор топика

Проблема с порядком удаления объектов. Parent'ы удаляются раньше child'ов и удаляют их в своих деструкторах. Но объекты стековые, что и вызывает сегфолт. Либо выделяй память динамически, либо поменяй порядок переменных так, чтобы child'ы удалялись первыми.

equeim ★★
()
Ответ на: комментарий от Xintrea

Не статическому, а стековому. Статический это совсем другое.

Можно, нужно посто поменять порядок переменных так, что были parent'ы были сверху. Тогда child'ы будут удаляться первыми, и сегфолта не будет.

equeim ★★
()
Ответ на: комментарий от equeim

Не статическому, а стековому. Статический это совсем другое.

Да, я неправильно написал.


Можно, нужно посто поменять порядок переменных так, что были parent'ы были сверху.

Имеется в виду поменять порядок в хидерах?

Так у меня парентом является сам объект, а чилдами являются стековые члены этого же объектного класса. Как я смогу поменять порядок?

Xintrea ★★★★★
() автор топика
Ответ на: комментарий от Xintrea

Когда ты запихиваешь виджеты в другой виджет и layout, он автоматически становится его child'ом. Т.е. у тебя QToolButton'ы - child'ы QToolBar'ов, а они - child'ы QVBoxLayout. Все они создаются на стеке внутри одного класса и удаляются в порядке, обратном тому, в котором они расположены в определении класса. Выходит так, что QVBoxLayout удаляется первым, и пытается удалить свои child'ы QToolBar, которые созданы на стеке, что приводит к сегфолту.

И да, тебе не нужно делать setParent(this) для стековых переменных, они удаляются автоматически.

equeim ★★
()
Последнее исправление: equeim (всего исправлений: 2)
Ответ на: комментарий от Xintrea

Не знаю внутренностей Qt. Похоже, что так. В любом случае просто посмотри, как советуют делать в мануале.

i-rinat ★★★★★
()
Ответ на: комментарий от equeim

Когда ты запихиваешь виджеты в другой виджет и layout, он автоматически становится его child'ом. Т.е. у тебя QToolButton'ы - child'ы QToolBar'ов, а они - child'ы QVBoxLayout. Все они создаются на стеке внутри одного класса и удаляются в порядке, обратном тому, в котором они расположены в определении класса. Выходит так, что QVBoxLayout удаляется первым, и пытается удалить свои child'ы QToolBar, которые созданы на стеке, что приводит к сегфолту.

Ага, примерно к этому я щас и пришел.


И да, тебе не нужно делать setParent(this) для стековых переменных, они удаляются автоматически.

В моем случае нужно, потому что кнопки и другие крутилки имеют object-имена и работа с ними при размещении на тулбаре происходит по имени, через поиск Qt-объекта в момент инициализации тулбара.

Xintrea ★★★★★
() автор топика
Ответ на: комментарий от Xintrea

Когда ты запихиваешь виджеты в другой виджет и layout, он автоматически становится его child'ом. Т.е. у тебя QToolButton'ы - child'ы QToolBar'ов, а они - child'ы QVBoxLayout. Все они создаются на стеке внутри одного класса и удаляются в порядке, обратном тому, в котором они расположены в определении класса. Выходит так, что QVBoxLayout удаляется первым, и пытается удалить свои child'ы QToolBar, которые созданы на стеке, что приводит к сегфолту.

В общем, в заголовке разместил перечень объектов так:

- Слой, содержащий тулбары
- Тулбары
- Кнопки и крутилки

Раньше было наоборот, но я и предположить не мог, что последовательность описания объектов в хедере может иметь хоть какое-то значение.

Xintrea ★★★★★
() автор топика
Ответ на: комментарий от Xintrea

И не зря. И не только в мануалах, но и в книгах сказано, что надо тебе использовать динамическое выделение памяти когда ты сталкиваешься с деревом классов Qt. Так надо и все, создатели фреймворка говорят так прыгать. Qt сам за тебя разрулит когда и каких child-ов parent-ов удалять. Он обожает рулить, как и любой фреймворк.

blokant ★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.