Вопросы по С++ 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, часто могут решить вашу проблему с рекреацией ресурсов, если вы можете передать ссылку на оригинал вокруг вашего кода.