0 added
0 removed
Original
2026-01-01
Modified
2026-03-10
1
<p>Теги: с++, имплементация, модификаторы virtual, sdk, интерфейсные методы, heap, инстанциированный объект класса, переполнениt памяти, деструктор, брейкпоинт в деструктор, виртуальные функции класса</p>
1
<p>Теги: с++, имплементация, модификаторы virtual, sdk, интерфейсные методы, heap, инстанциированный объект класса, переполнениt памяти, деструктор, брейкпоинт в деструктор, виртуальные функции класса</p>
2
<p><em>Делюсь с вами рассказом своего коллеги - разработчика С++. Уверен, информация пригодится вам в проектах!</em></p>
2
<p><em>Делюсь с вами рассказом своего коллеги - разработчика С++. Уверен, информация пригодится вам в проектах!</em></p>
3
<p>Наконец-то пришёл долгожданный SDK от компании-партнёра. Проблем с интеграцией в наш проект не возникло. Началась работа над имплементацией на нашей стороне.</p>
3
<p>Наконец-то пришёл долгожданный SDK от компании-партнёра. Проблем с интеграцией в наш проект не возникло. Началась работа над имплементацией на нашей стороне.</p>
4
<p>Собственно говоря, всё представлялось исключительно чётким и ясным: мы наследуем базовый класс из SDK нашего партнёра, имплементируем необходимые интерфейсные методы, добавляем свои... Работа заняла пару-тройку дней. Настало время тестирования.</p>
4
<p>Собственно говоря, всё представлялось исключительно чётким и ясным: мы наследуем базовый класс из SDK нашего партнёра, имплементируем необходимые интерфейсные методы, добавляем свои... Работа заняла пару-тройку дней. Настало время тестирования.</p>
5
<p>Практически сразу же обнаружилась серьёзнейшая проблема: размер<strong>кучи (heap)</strong>безразмерно рос, приложение падало после нескольких минут работы.</p>
5
<p>Практически сразу же обнаружилась серьёзнейшая проблема: размер<strong>кучи (heap)</strong>безразмерно рос, приложение падало после нескольких минут работы.</p>
6
<h2>Стали всё проверять и перепроверять</h2>
6
<h2>Стали всё проверять и перепроверять</h2>
7
<p>Наследуем базовый класс, имплементируем интерфейсные методы, несколько методов переопределяем, инстанциированный объект класса передаём обратно в SDK. Всё делается буквально по букварю, а приложение вылетает из-за переполнения памяти.</p>
7
<p>Наследуем базовый класс, имплементируем интерфейсные методы, несколько методов переопределяем, инстанциированный объект класса передаём обратно в SDK. Всё делается буквально по букварю, а приложение вылетает из-за переполнения памяти.</p>
8
<p>Обратились за помощью в компанию-партнёр. Ответ не заставил себя долго ждать: у нас всё протестировано и отлично работает. Оснований не доверять нет.</p>
8
<p>Обратились за помощью в компанию-партнёр. Ответ не заставил себя долго ждать: у нас всё протестировано и отлично работает. Оснований не доверять нет.</p>
9
<h2>Мистика какая-то!</h2>
9
<h2>Мистика какая-то!</h2>
10
<p>Расход памяти растёт при создании и удалении объектов класса, который наследует базовый класс из SDK. Тогда как по смыслу, объекты должны обрабатываться и удаляться из памяти.</p>
10
<p>Расход памяти растёт при создании и удалении объектов класса, который наследует базовый класс из SDK. Тогда как по смыслу, объекты должны обрабатываться и удаляться из памяти.</p>
11
<p>Объекты создаются. Помещаются в очередь. Асинхронно обрабатываются. После чего удаляются из очереди, и память должна освобождаться. Записанные логи полностью всё подтверждают.</p>
11
<p>Объекты создаются. Помещаются в очередь. Асинхронно обрабатываются. После чего удаляются из очереди, и память должна освобождаться. Записанные логи полностью всё подтверждают.</p>
12
<p>Почему же тогда расход памяти постоянно только растет? Где происходит утечка памяти?</p>
12
<p>Почему же тогда расход памяти постоянно только растет? Где происходит утечка памяти?</p>
13
<h2>Продолжаем расследовать</h2>
13
<h2>Продолжаем расследовать</h2>
14
<p>Добавляем деструктор в наш класс, наследующий базовый класс из SDK. При этом не забываем прописать модификатор virtual. Компилируем и собираем проект заново. Ставим брейкпоинт в наш деструктор, пусть он и пустой. Запускаем приложение на исполнение... И не видим ни одного останова в нашем деструкторе!</p>
14
<p>Добавляем деструктор в наш класс, наследующий базовый класс из SDK. При этом не забываем прописать модификатор virtual. Компилируем и собираем проект заново. Ставим брейкпоинт в наш деструктор, пусть он и пустой. Запускаем приложение на исполнение... И не видим ни одного останова в нашем деструкторе!</p>
15
<h2>Как же так?</h2>
15
<h2>Как же так?</h2>
16
<p>Ведь в любой литературе по C++ чётко прописывается, что при удалении объекта класса из памяти, будет первым вызван его собственный деструктор, а потом деструктор базового класса...</p>
16
<p>Ведь в любой литературе по C++ чётко прописывается, что при удалении объекта класса из памяти, будет первым вызван его собственный деструктор, а потом деструктор базового класса...</p>
17
<p>Что ж, это объясняет неограниченный рост расхода памяти. Но чёрт возьми, в логах явно видно, что объекты удаляются из памяти, размер очереди из объектов нашего класса не растёт неограниченно!</p>
17
<p>Что ж, это объясняет неограниченный рост расхода памяти. Но чёрт возьми, в логах явно видно, что объекты удаляются из памяти, размер очереди из объектов нашего класса не растёт неограниченно!</p>
18
<p>Однако, деструктор нашего класса не вызывается. Это больше, чем факт, потому что так происходит на самом деле... Предпринимаем несколько раундов переговоров с компанией-партнёром - ответ один и тот же: у нас всё протестировано и работает.</p>
18
<p>Однако, деструктор нашего класса не вызывается. Это больше, чем факт, потому что так происходит на самом деле... Предпринимаем несколько раундов переговоров с компанией-партнёром - ответ один и тот же: у нас всё протестировано и работает.</p>
19
<p>И тут вдруг буквально краем глаза, замечаем, что в заголовочном файле из SDK деструктор базового класса определён без модификатора virtual.</p>
19
<p>И тут вдруг буквально краем глаза, замечаем, что в заголовочном файле из SDK деструктор базового класса определён без модификатора virtual.</p>
20
<h2>Семён Семёныч, ну как же так?!</h2>
20
<h2>Семён Семёныч, ну как же так?!</h2>
21
<p>Мы наследуем этот базовый класс, инстанциируем его, полученный объект передаём обратно в SDK, что вызывает потерю типа; объект приводится к базовому классу. Впоследствии, когда объект удаляется, вызывается только деструктор базового класса, освобождается не вся память!</p>
21
<p>Мы наследуем этот базовый класс, инстанциируем его, полученный объект передаём обратно в SDK, что вызывает потерю типа; объект приводится к базовому классу. Впоследствии, когда объект удаляется, вызывается только деструктор базового класса, освобождается не вся память!</p>
22
<p>Добавление модификатора virtual к деструктору нашего класса ровным счётом ничего не даёт, т.к. SDK скомпилирован таким образом, что деструктор базового класса не занесён в таблицу виртуальных функций класса!</p>
22
<p>Добавление модификатора virtual к деструктору нашего класса ровным счётом ничего не даёт, т.к. SDK скомпилирован таким образом, что деструктор базового класса не занесён в таблицу виртуальных функций класса!</p>
23
<h2>Finita la commedia!</h2>
23
<h2>Finita la commedia!</h2>
24
<p>Объясняем проблему компании-партнёру, ждём новый релиз SDK… Вот такой коварный бывает C++! Не забывайте добавлять модификаторы virtual к методам базового класса и его деструктору! Это поможет избежать многих неочевидных проблем и часов отладки!</p>
24
<p>Объясняем проблему компании-партнёру, ждём новый релиз SDK… Вот такой коварный бывает C++! Не забывайте добавлять модификаторы virtual к методам базового класса и его деструктору! Это поможет избежать многих неочевидных проблем и часов отладки!</p>
25
<p><em>Есть вопрос? Напишите в комментариях!</em></p>
25
<p><em>Есть вопрос? Напишите в комментариях!</em></p>
26
26