2017-01-12 3 views
3

tl; dr - Состав кажется правильным выбором для моей ситуации, но я бы хотел избежать накладных расходов на распределение кучи для каждого объекта, который я собираю вместе. Какие-нибудь хорошие методы?Избегайте дополнительных распределений кучи с композицией (над наследованием)?

У меня есть установка, которая выглядит как:

class Foo { 
    public: 
    Foo(unique_ptr<FooEvaluator> evaluator) 
    : evaluator_(std::move(evaluator)), 
     common_state_() {} 

    void FooMethod() { 
     // Perform some common processing. 
     evaluator.DoSomething(); 
    } 
    private: 
    unique_ptr<FooEvaluator> evaluator_; 
    Bar common_state_; 
} 

class FooEvaluator { 
    public: 
    virtual void DoSomething() = 0; 
} 

class FooEvaluatorImpl : public FooEvaluator { 
    public: 
    FooEvaluatorImpl(type1 arg) {...} 

    void DoSomething() override {...} 
} 

class FooEvaluatorImplVariant : public FooEvaluator { 
    public: 
    FooEvaluatorImplVariant(type2 arg) {...} 

    void DoSomething() override {...} 
} 

Грубо: У меня есть Foo, который делает некоторые общие обработки, а затем использует FooEvaluator. У меня есть несколько реализаций FooEvaluator, каждая из которых имеет свою логику. Обычная обработка Foo достаточно нетривиальна, что имеет смысл попытаться избежать ее дублирования.

Я обеспокоен тем, что теперь я должен выделить два объекта вместо одного, если бы у меня был монолитный Foo, содержащий как общую логику, так и логику оценки.

Одно смутно разумное промежуточное поле, о котором я могу думать, это унаследовать FooEvaluator от Foo и добавить DoSomething() в качестве частного виртуального метода на Foo. Это немного злоупотребление наследованием imo (вы не можете сказать, что FooEvaluator "является" Foo), но он получит мой общий код и избежит выделения двойной памяти.

Любые мысли?

+0

все имеют примерно такой же размер Foo оценщика? Если это так, я бы превратил их в вариант std :: variant или boost: variant и имел функцию посетителя, которая реализует то, что обычно является интерфейсом виртуальных функций. – RyanP

+0

Можете ли вы заменить виртуальное наследование на crtp, т. Е. Составить композицию во время компиляции? Это убивает уникальные_ptr, распределения кучи и vtables в обмен на потерю стирания типа (или перемещение его на уровень). –

+0

Я не уверен, что вы подразумеваете под «композицией»? Можете ли вы привести пример? Я не понимаю, как состав может привести к множественным распределениям. (Наверное, мое собственное невежество!) –

ответ

2

Вы можете сделать что-то подобное с шаблоном:

template <typename Evaluator> 
class Foo { 
public: 
    template <typename ... Ts> 
    Foo(Ts&&... args) 
    : evaluator_(std::forward<Ts>(args)...), 
     common_state_() 
    {} 

    void FooMethod() { 
     // Perform some common processing. 
     evaluator.DoSomething(); 
    } 
private: 
    Evaluator evaluator_; 
    Bar common_state_; 
}; 
+0

Это похоже на лучшее решение. Я оставлю его непринятым на один день, чтобы узнать, подсказывает ли кто-нибудь другое. – mfrankli

-1

Ну, так как я понятия не имею о Foo по сложности/подробности, я бы предложил сделать ему инлайн функцию, а затем вы можете вызвать его из нескольких экземпляров FooEvaluator, для того, чтобы выполнить общую обработку, прежде чем делать особенности.

+0

Извините за недостаток деталей - у Foo есть состояние, которое, к сожалению, означает, что это не сработает (я думаю?) – mfrankli

+0

Да, вы правы. Нельзя переместить «общую обработку» в базовый класс FooEvaluator; удалить Foo, сохранить только класс Bar, содержащий состояние, и заставить экземпляры FooEvaluators иметь Bar *, чтобы ссылаться на имеющуюся информацию о состоянии, необходимую для выполнения обработки? –

Смежные вопросы