#c #templates #design-patterns
#c #шаблоны #дизайн-шаблоны
Вопрос:
Я столкнулся со следующей проблемой: как инициализировать статический прокси, который содержит объект, предоставленный в качестве параметра шаблона.
Вот мой фрагмент кода, который показывает ситуацию:
template<class SharedState>
class TToolbar : public ImplCustomToolBar
{
public:
virtual const ICustomToolbarsSharedState* const GetSharedState() const { return amp;sharedState_.Get();}
private:
// intent: static constructor for static SharedState initialization
class SharedStateHolder
{
public:
SharedStateHolder()
{
ToolbarsSharedState_.Initialize();
}
const SharedStateamp; Get() const { return ToolbarsSharedState_;}
private:
SharedState ToolbarsSharedState_;
};
static SharedStateHolder sharedState_;
};
// I need TToolbar::SharedStateHolder sharedState_;
Поскольку неизвестно, какой параметр шаблона будет предоставлен, я не могу использовать явную специализацию шаблона для инициализации статического члена.
Итак, единственное решение, которое я нашел: клиенты TToolBar должны сами выполнять инициализацию. Но я думаю, что это подверженное ошибкам решение.
Вопрос: какие слабые места есть в моем решении и каков правильный способ справиться с такой ситуацией?
Комментарии:
1. Это проблема внедрения зависимостей, посмотрите, как
std::vector::emplace
ее решить, или общий шаблон .2. @didierc О, большое вам спасибо, я никогда не слышал об этом шаблоне. Я знаю все шаблоны из Gof4 и подумал, что этого достаточно. Не могли бы вы дать мне несколько ссылок на другие шаблоны? Если вы опубликуете свой ответ, я мог бы принять его (конечно, после того, как я ознакомлюсь с шаблоном внедрения)
3. @didierc Я читал о внедрении зависимостей, я вспоминаю, что сталкивался с этим раньше. Как этот шаблон можно применить к моей проблеме?
Ответ №1:
Вы можете использовать статическую локальную переменную функции-члена:
template<class SharedState>
class TToolbar : public ImplCustomToolBar
{
virtual const ICustomToolbarsSharedState* const GetSharedState() const
{
static SharedStateHolder sharedState_;
return amp;sharedState_.Get();
}
};
Компилятор отвечает за то, чтобы статика была уникальной.
Или, если кто-то хочет избавиться от этого класса Holder:
template<class SharedState>
class TToolbar : public ImplCustomToolBar
{
virtual const ICustomToolbarsSharedState* const GetSharedState() const
{
static SharedState sharedState_;
static bool initialized = false;
if (!initialized)
{
sharedState_.Initialize();
initalized = true;
}
return amp;sharedState_;
}
};
Комментарии:
1. Спасибо. Я рассматривал такие решения. Мне нравится первый, я думаю, что он отвечает на часть моего вопроса «правильный способ решения такой ситуации», но оставшийся вопрос все еще не раскрыт (и это самое важное для меня). 1 Но я не могу принять ваш ответ.
Ответ №2:
Один из способов решить вашу проблему — делегировать создание TToolBar
factory , который знает об этом общем состоянии (по сути, это то, что SharedStateHolder
делает), и передать его защищенному конструктору of TToolBar
, который может использовать только эта фабрика, будучи friend
с. TToolBar
Объекты, нуждающиеся в такой панели инструментов, должны пройти через фабрику, чтобы получить ее.
template<typename SharedState>
class TToolBar {
friend class TToolBarFactory;
// ...
protected:
TToolBar(SharedState *shared){
// ...
}
// all other constructor also protected
};
class TToolBarFactory{
public:
TToolBarFactory();
TToolBar<SharedStateImpl> *makeToolBar();
// other toolbar pseudo constructors
};
Теперь SharedState
построение делегируется выделенному классу, который должен быть одноэлементным, чтобы гарантировать, что все панели инструментов имеют одинаковое состояние.
В качестве альтернативы, поскольку SharedState
экземпляр является статическим, вы можете просто создать статический метод для установки этой переменной один раз для всех и заставить фабрику вызывать ее при создании экземпляра. Тогда вам не нужно было бы передавать его TToolBar
конструктору (как я сделал выше) и разрешать другим объектам создавать их напрямую.
Мы можем рассматривать TToolBarFactory
как конструктор для TToolBar
класса, а не его экземпляров.
Обратите внимание, что эта проблема ортогональна тому факту, который TToolBar
параметризован на SharedState
: вы могли бы заставить фабрику напрямую предоставлять специализированные экземпляры TToolBar
(как я сделал) или обобщить его на шаблон с конструктором a la std::vector::emplace
, который шаблонируется на основе содержащихся параметров конструктора данных.
template <typename SharedState>
class TToolBarFactory {
public:
template <class... args>
TToolBarFactory(Args...args){
TToolBar<SharedState>::setSharedState(new SharedState(args...));
// remaining factory init code.
}
// this could also be a variadic templated function
TToolBar<SharedState> *makeToolBar(){
return new TToolBar<SharedState>();
}
};
// example
class SharedStateImpl {
public:
SharedStateImpl(int i, float f){}
};
// the factory instance
TToolBarFactory<SharedStateImpl> factory(42,3.14);
int main(){
TToolBar<SharedStateImpl> *toobar = factory.makeToolBar();
}
Если я понял вашу проблему, на самом деле речь идет о том, что она была SharedState
создана снаружи TToolBar
, потому что.вы не хотите, чтобы этот класс делал какие-либо предположения о SharedState
конструкторах. Мое решение немного тяжеловесно. Вы определенно могли бы сделать то же самое, просто создав SharedStateHolder
параметр шаблона TToolBar
и сохранив там все детали инициализации.
Комментарии:
1. спасибо, у меня возникла идея о внедрении зависимостей с помощью конструктора, я думаю, что ваше решение представляет некоторую чрезмерную сложность. Моя цель — уменьшить сложность. Спасибо за хорошее объяснение! 1 за ответ, но я не могу его принять, поскольку он лишь частично отвечает на мой вопрос (проверьте заголовок вопроса)
2. Да, я понял, что немного увлекся, особенно после того, как увидел другой ответ. Смотрите последний абзац моего ответа.
3. это моя ошибка, я пропустил слово static в заголовке вопроса, я имел в виду «Может ли инициализация статического члена шаблонного класса быть делегирована клиенту». Итак, ваш ответ решил вопрос. Спасибо!