Хорошо или плохо Идиома C ++ - Объекты, используемые исключительно для конструктора / деструктора? - PullRequest
9 голосов
/ 12 января 2009

У меня есть несколько классов, которые ничего не делают, кроме как в своих конструкторах / деструкторах. Вот пример

class BusyCursor 
{
  private:
    Cursor oldCursor_;

  public:

    BusyCursor()
    {
      oldCursor_ = CurrentCursor();
      SetCursor(BUSY_CURSOR);
    }
    ~BusyCursor()
    {
      SetCursor(oldCursor_);
    }
}

// example of use
    void DoSlowThing
    {
      BusyCursor busy;
      ... do something time-consuming  ...
    }

Я немного обеспокоен читаемостью в будущем. Я слишком "хитрый" здесь с переменной ("занят"), которая никогда не используется в коде? Может ли какой-нибудь инструмент статического анализа предложить удалить их, или эта идиома достаточно распространена, чтобы о ней не беспокоиться?

Ответы [ 9 ]

36 голосов
/ 12 января 2009

Этот метод очень распространен и известен как шаблон проектирования: Получение ресурсов - инициализация (RAII) .

Я бы без колебаний использовал этот шаблон проектирования вообще.

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

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


«Может ли какой-нибудь инструмент статического анализа предложить удалить их?»

  • Ни один инструмент статического анализа не воспримет это как проблему.
  • Предупреждения компилятора не выдаются
  • Никакая оптимизация компилятора не вызовет никаких проблем.

Причина в том, что объект создан и вызван конструктор / деструктор. Так что это не ссылочная переменная.

10 голосов
/ 12 января 2009

Как уже говорили другие, это хороший стиль C ++. Чтобы улучшить читабельность, я всегда добавляю к таким классам только для RAII Scoped (например, ScopedBusyCursor), чтобы с первого взгляда было ясно, какова цель этого класса.

7 голосов
/ 12 января 2009

Это хорошо известная и хорошая идиома C ++, как и все остальные.

Чтобы было ясно, что классы предназначены для использования только внутри области, а не для перемещения между различными областями, было бы целесообразно сделать их не копируемыми. Это можно сделать вручную, добавив нереализованный личный конструктор копирования и оператор копирования. Более короткий и понятный способ - извлечь класс из boost :: noncopyable :

#include <boost/noncopyable.hpp>
class BusyCursor : public boost::noncopyable // for scoped use only
{
    // ...
};
4 голосов
/ 12 января 2009

Возможно, не использовать этот шаблон - плохая идиома. Когда вы не используете RAII, ваш код выглядит так:

void func() {
    Cursor oldCursor = CurrentCursor();
    SetCursor(BUSY_CURSOR);
    try {
        do_slow_stuff();
        SetCursor(oldCursor);
    } catch (...) {
        SetCursor(oldCursor);
        throw;
    }
}

Неужели вы действительно думаете, что это засоряется в вашем коде лучше для обслуживания?

4 голосов
/ 12 января 2009

Это хорошая и часто используемая идиома.

Это лучше, чем любая альтернатива, например, даже если ваш какой-то трудоемкий код выдает исключение, деструктор ~BusyCursor все равно будет вызываться.

3 голосов
/ 12 января 2009

Другие уже упоминали, что это классический RAII. Я хочу добавить, что это одна из лучших вещей в C ++. Очень немногие другие языки поддерживают его или, по крайней мере, поддерживают его должным образом (даже использование C # конструкции не так хорошо, так как бремя по-прежнему лежит на клиентском коде - см. Запись в моем блоге об этом ).

Он стал настолько тесно связан с C ++, что вы должны быть уверены, что любой, кто его читает, знаком с ним - и если нет, то должен быть.

3 голосов
/ 12 января 2009

Нет нормального инструмента статического анализа, который бы предлагал удалить переменную, потому что используется . Это имеет эффект, потому что его конструктор и деструктор вызваны. Вы должны быть в полной безопасности.

2 голосов
/ 12 января 2009

Я обычно называю это "охранником". На мой взгляд, это демонстрирует одну из самых сильных сторон C ++ (детерминированная обработка ресурсов). Это одна из вещей, по которой я скучаю больше всего при работе на языках, собираемых мусором.

0 голосов
/ 17 января 2009

Вы также можете пойти с чем-то вроде ScopeGuard от Андрея Александреску и Петру Марджиняна. Ваш образец будет выглядеть примерно так:

void DoSlowThing
{     
    Cursor oldCursor = CurrentCursor();
    SetCursor(BUSY_CURSOR);
    ON_BLOCK_EXIT(SetCursor, oldCursor);
    ... do something time-consuming  ...
}

Это облегчает выполнение одноразовых операций типа RAII без необходимости создавать новый класс для каждого из них. Однако для вашего примера Cursor, поскольку этот класс вы, вероятно, будете использовать много раз, вам, вероятно, лучше использовать выделенный класс.

...