C ++: Обязательно ли помещать стратегию в оболочку, чтобы избежать появления новых / удалений? - PullRequest
2 голосов
/ 25 января 2012

Итак, я обнаружил радости стратегий.

class Strategy
{
public:
    virtual void action() = 0;
};

class FooStrategy : public Strategy
{
    virtual void action() { /* do some stuff */ };
};

class BarStrategy : public Strategy
{
    virtual void action() { /* do different stuff */ };
};

void computation()
{
    Strategy *strategy;
    if ( /* some decision logic */ )
        strategy = new FooStrategy();
    else
        strategy = new BarStrategy();

    //later on...
    //big loop! don't want any overheads beyond what if/else would give
    for (int i=0;i<100000000;++i)
    {
        //...various other code and then:
        strategy->action();
    }

    //...other code and possibly more strategy->action() calls, then finally:
    delete strategy;
}

Все это написано для замены предложения if/else логикой принятия решений каждый раз, когда мне нужно action().Мы можем предположить, что это читается более ясно, потому что именно поэтому мы используем это.Что касается накладных расходов, таблица виртуальных функций для strategy, несомненно, попадет в кэш процессора, поэтому здесь должно быть мало накладных расходов по сравнению с if/else, верно?

Однако.C ++ не допускает абстрактные базовые классы в стеке, предположительно, поскольку их размер неизвестен, поэтому strategy должен жить в куче, вводя опасные операторы new и delete.Конечно, я мог бы написать класс-обертку, чтобы безопасно обрабатывать создание и удаление стратегий, и мы надеемся, что это будет полностью оптимизировано компилятором.Но мне интересно, есть ли более элегантный способ поместить Strategy в стек, обеспечивая тем самым автоматическое удаление, учитывая, что все стратегии имеют одинаковый размер?

Редактировать Спасибо всем за ответы, я думаю, что они все очень хороши и просвещают в отношении различных способов ведения дел.Я собираюсь воздержаться от принятия любого человека как правильного.Я рекомендую всем будущим читателям рассмотреть их все и быть либеральным с голосами поддержки!

Ответы [ 5 ]

2 голосов
/ 25 января 2012

Почему бы просто не использовать умный указатель?

std::auto_ptr<Strategy> strategy;
if ( /* some decision logic */ )
    strategy.reset(new FooStrategy());
else
    strategy.reset(new BarStrategy());

Теперь вам не нужно ничего delete.

C ++ 11 предлагает std::unique_ptr и std::shared_ptr какЧто ж.Кроме того, Boost предлагает также shared_ptr и boost::scoped_ptr (что лучше, чем auto_ptr, поскольку копирование auto_ptr может вызвать проблемы. scoped_ptr не подлежит копированию).

1 голос
/ 25 января 2012

Вы можете использовать вариант шаблона ScopeGuard:

const Strategy& strategy = FooStrategy();

Ссылка на констант (и она должна быть константной) может быть связана с временным, продлевая время жизни временного.Это позволяет вам безопасно и эффективно хранить все в стеке, обращаясь к объекту по его базовому типу (как const Strategy&).

1 голос
/ 25 января 2012

Вот что я бы сделал:

  • Создание конструкторов для класса «Стратегия» и всех других классов private
  • Написать фабричную функцию, которая возвращает умный указатель наStrategy класс с подсчетом ссылок или передачей семантики владения.Это должно содержать ваше if условие.
  • У каждого из ваших Strategy подклассов есть эта функция как friend.

Таким образом, вы гарантируете, что ваш Strategy объекты создаются только как части умного указателя.Недостатком является то, что вам нужно будет добавить объявление friend ко всем текущим и будущим подклассам Strategy.

1 голос
/ 25 января 2012

Вы можете использовать умный указатель, как это предлагают другие, или вы можете использовать способ в стиле C:

void fooStrategy() { /* do some stuff */ }

void barStrategy() { /* do other stuff */ }

void computation()
{
    typedef void (*Strategy)();
    Strategy strategy;
    if ( /* some decision logic */ )
        strategy = &fooStrategy;
    else
        strategy = &barStrategy;

    //later on...
    //big loop! don't want any overheads beyond what if/else would give
    for (int i=0;i<100000000;++i)
    {
        //...various other code and then:
        strategy();
    }

    //...other code and possibly more strategy() calls
}

Изменить

Илив том же направлении мысли.Пока ваши стратегии не имеют состояния, почему бы не иметь единый глобальный объект для каждой стратегии - тогда вам не придется беспокоиться об удалении:

class FooStrategy : public Strategy
{
    virtual void action() { /* do some stuff */ };
} FooStrategyObject;

class BarStrategy : public Strategy
{
    virtual void action() { /* do different stuff */ };
} BarStrategyObject;

/* ... */

if ( /* some decision logic */ )
    strategy = &FooStrategyObject;
else
    strategy = &BarStrategyObject;
1 голос
/ 25 января 2012

Если вы можете решить это во время разговора, вы можете использовать шаблоны:

template <typename TPolicyImpl>
void computation()
{
    TPolicyImpl policy;
 ...
}

и назовите это как:

  computation<FooPolicy>()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...