Вопросы по С++ RAII
Итак, я понимаю, что для правильной реализации RAII, если я вызываю CreateFont
, я бы обернул это в класс с CreateFont
в конструкторе и DeleteObject
в деструкторе, чтобы он очистил его когда он выходит за рамки.
Первый вопрос: не закончится ли я с ALOT классов? Тем более, что класс имеет только конструктор и деструктор.
Второй вопрос: что, если я вызываю класс CreateFont в WndProc, который выходит из сферы действия постоянно. Так я должен делать все мои звонки в CreateFont
или как LoadBitmap
в WndMain?
Я использовал для вызова этих функций в WM_CREATE
и очистки их в WM_DESTROY
.
Ответы
Ответ 1
Вы можете избежать много повторяющейся работы, используя шаблон, который поможет вам. Например, если вы используете boost::shared_ptr
, вы можете:
#include <boost/shared_ptr.hpp>
#include <functional>
struct Font;
Font *createFont();
void deleteFont(Font*);
int main() {
boost::shared_ptr<Font> font(createFont(), std::ptr_fun(deleteFont));
}
Что избавляет вас от написания пользовательского класса для управления ресурсом. Если boost и TR1 или новее недоступны для вас, все же возможно реализовать что-то подобное и универсальное для оказания помощи.
boost::shared_ptr
правильно подсчитана ссылка, поэтому, если вы хотите создать ее где-нибудь и "продвинуть" ее, чтобы жить дольше, вы можете сделать это, скопировав ее где-то дольше, до ее смерти.
Ответ 2
Первый вопрос: не закончится ли я с ALOT классов? Тем более, что класс имеет только конструктор и деструктор.
Да, но есть несколько моментов для рассмотрения:
- Это проблема? Классы будут небольшими и легко читаемыми и понятными,
- вы можете повторно использовать многие из них (например, существует множество функций Win32, которые создают объекты
HANDLE
, и все они закрыты одинаково (с CloseHandle
), поэтому вы можете повторно использовать для тех же классов.
- вы можете использовать смарт-указатель или какую-либо другую общую оболочку, чтобы заполнить большую часть шаблона. Самые популярные классы интеллектуальных указателей позволяют указать пользовательскую функцию удаления.
Второй вопрос: что, если я вызываю класс CreateFont в WndProc, который выходит из сферы действия постоянно.
Храните его в том месте, где оно не выйдет из сферы действия преждевременно.:)
Здесь интеллектуальные указатели могут быть полезны снова. Например, shared_ptr
можно использовать для сохранения живого шрифта до тех пор, пока на нем не будет указана хотя бы одна shared_ptr
. Затем вы можете просто передать его из функции в какое-то общее более долгое время.
В противном случае, до тех пор, пока вы реализуете конструктор копирования и оператор присваивания (в С++ 11 вам может понадобиться реализовать конструктор перемещения и переместить назначение вместо этого) для вашего класса RAII, его можно скопировать (или перенести) безопасно туда, где вы хочу сказать, даже если он был создан в меньшей области.
Ответ 3
Первый вопрос: не закончится ли я с ALOT классов? Especialy, поскольку класс имеет только конструктор и деконструктор
Если вам не нравится количество классов, которые вам нужно создать для каждого типа объекта, вы можете создать один класс RAII, который принимает параметр HGDIOBJ в конструкторе и вызывает DeleteObject в деструкторе. Затем этот класс можно использовать для всех разных объектов GDI. Например:
class GDIObject
{
public:
HGDIOBJ GdiObject;
GDIObject( HGDIOBJ object )
: GdiObject( object )
{
}
~GDIObject()
{
DeleteObject( GdiObject );
}
}
...
GDIObject font( CreateFont( 48, 0, 0, 0, FW_DONTCARE, false, true, false, DEFAULT_CHARSET, OUT_OUTLINE_PRECIS, CLIP_DEFAULT_PRECIS, CLEARTYPE_QUALITY, VARIABLE_PITCH, TEXT("Impact") ) );
Второй вопрос: что, если я вызываю класс CreateFont в WndProc, который выходит из сферы действия постоянно. Так я должен делать все мои звонки в CreateFont или как LoadBitmap в WndMain? Я использовал для вызова этих функций в WM_CREATE и очистки их в WM_DESTROY.
Для элементов, которые должны оставаться в памяти дольше, чем область функций, вам нужно будет разместить их на глобальном уровне.
Ответ 4
Если вы собираетесь работать с твердым RAII, есть несколько вариантов сокращения количества классов, которые вам понадобятся - например, некоторые повышающие интеллектуальные указатели (shared_ptr специально) позволяют вам предоставлять свою собственную функцию для вызова, когда указатель выходит за рамки.
В противном случае да - у вас будет класс для любого ресурса, для которого требуется явное освобождение - и, хотя это боль в коде, он сэкономит вам массу времени на отладку в долгосрочной перспективе (особенно в групповых проектах). Я бы все же сказал использовать RAII на основе ваших собственных чувств в ситуации, хотя это замечательная идея использовать его повсюду, но в некоторых случаях при преобразовании старого кода это может быть огромная работа по исправлению всех цепочек вызовов вверх использовать его.
Еще одна вещь: я не слишком знаком с логикой GUI, с которой вы работаете, поэтому я не буду туда специально... но вам нужно будет изучить, как ваши ресурсы должны быть скопированы и как долго они должны поддерживаться. Будут ли ваши контейнеры RAII подсчетом ссылок или будут ли они иметь семантику ценности (копии)? Ссылки, подсчитывающие интеллектуальные указатели, такие как shared_ptr, часто могут решить вашу проблему с рекреацией ресурсов, если вы можете передать ссылку на оригинал вокруг вашего кода.