#c #c 11 #singleton
Вопрос:
Я кодирую класс с одноэлементным шаблоном.
class GroupListDAO {
public:
static GroupListDAO* getInstance() {
static GroupListDAO groupListDAO;
return amp;groupListDAO;
}
init(server::mysqldb::MysqlHelperTempalte* pHelper) {
mysqlHT = pHelper;
}
bool getUserHeartNum(uint32_t owner, uint32_tamp; totalNum);
bool setUserHeartNum(uint32_t owner, uint32_t totalNum, uint32_t update_time);
private:
MysqlHelperTempalte *mysqlHT;
GroupListDAO() = defau<
~GroupListDAO() = defau<
};
Как вы видите, этот класс используется для подключения к Mysql. Таким образом, данные mysqlHT
участника должны быть инициализированы перед вызовом любых других функций-членов.
Одним словом, пользователь класса должен использовать этот класс, как показано ниже:
GroupListDAO *p = GroupListDAO::getInstance();
p->init(XXX); // init must be called before calling any other member functions
p->getUserHeartNum(...);
p->setUserHeartNum(...);
Поэтому я думаю, есть ли способ заставить пользователя класса вызвать функцию init
. Это означает, что если код пользователя класса выглядит следующим образом:
GroupListDAO *p = GroupListDAO::getInstance();
p->getUserHeartNum(...);
p->setUserHeartNum(...);
Может возникнуть некоторая ошибка во время компиляции.
Ofc, вы можете сказать, что мы можем if (mysqlHT == nullptr) { throw exception; }
в других функциях-членах, но это было бы ошибкой во время выполнения, а не ошибкой во время компиляции.
РЕАЛЬНЫЙ СЛУЧАЙ
Один большой проект разрабатывается 5 разработчиками. Все они используют некоторые одноэлементные объекты. Но они должны сказать: Эй, я инициализировал это, вы, ребята, можете использовать это в своих кодах. Или, к сожалению, мы все забываем его инициализировать…
Комментарии:
1. почему
init
не вызываетсяgetInstance
как часть инициализации?2. @463035818_is_not_a_number Видите ли, я не хочу звонить
getInstance
с каким-либо параметром.getInstance
можно было бы звонить много раз, верно? ЕслиgetInstance
нужен какой-то параметр, я не думаю, что это хороший дизайн.3. Я понимаю, и я несколько раз сталкивался с одной и той же проблемой. Тем не менее, я думаю, что это можно было бы более четко упомянуть в вопросе.
4. общественные
init
функции также не являются хорошим дизайном. Синглтоны тоже оставляют желать лучшего.5. @463035818_is_not_a_number Говорит, что вы являетесь пользователем класса. Я думаю, что вы хотели бы выполнить инициализацию только один раз, после чего вы можете использовать ее в любом месте. Когда вы используете его, вы не хотите напоминать себе, что «Хорошо, я должен передать xxx
getInstance
«. Кроме того, передавать параметрgetInstance
кому-то глупо, потому что параметр будет использоваться только при первом вызовеgetInstance
.
Ответ №1:
Как указано в комментарии, вы можете использовать аргумент по умолчанию:
getInstance(server::mysqldb::MysqlHelperTempalte* pHelper = nullptr)
С одной стороны, нехорошо позволять вызывающему абоненту передавать параметр, но использовать его только при первом вызове. С другой стороны, ваш дизайн предполагает, что есть одно место, куда вы знаете, что это первый звонок getInstance
(вы же не хотите делать init
это дважды, верно?). Поэтому я предлагаю следующее:
class GroupListDAO {
public:
static GroupListDAO* createInstance(server::mysqldb::MysqlHelperTempalte* pHelper) {
return getInstance(pHelper);
}
static GroupListDAO* getInstance() {
return getInstance(nullptr);
}
private:
static GroupListDAO* getInstance(server::mysqldb::MysqlHelperTempalte* pHelper = nullptr) {
static GroupListDAO groupListDAO(pHelper);
return amp;groupListDAO;
}
};
GroupListDAO
возможно, должна быть оболочка фактического объекта, и он может вводить свой конструктор при вызове с помощью a nullptr
. Я не знаю простого способа создания ошибки компилятора.
PS: На самом деле я думаю, что у вас противоречивые требования. С одной стороны, вам нужен синглтон, который скрывает инициализацию от вызывающего абонента. Экземпляр может быть получен с помощью a getInstance
без необходимости заботиться об инициализации. С другой стороны, вы действительно хотите позаботиться об инициализации, потому что первый вызов getInstance
«особенный». И одноэлементный шаблон, и использование init
метода не являются идеальными идиомами в отдельности. При совместном использовании они хуже. Я не говорю, что это абсолютный отказ, но не слишком удивительно, что приходится идти на компромисс, когда следует придерживаться обоих подходов.
Ответ №2:
Если вы действительно хотите получить ошибку компиляции, вам придется избавиться от своего одноэлементного шаблона:
class GroupListDao
{
public:
GroupListDao(...) { /* do your initialization here */ }
bool getUserHeartNum(uint32_t owner, uint32_tamp; totalNum);
bool setUserHeartNum(uint32_t owner, uint32_t totalNum, uint32_t update_time);
// ...
}
Таким образом, вы гарантируете, что любой экземпляр GroupListDao
был инициализирован. Затем вам придется передать этот экземпляр в своем коде. Как следствие, вы больше не можете быть уверены, что GroupListDao
во время компиляции в вашем приложении есть только один экземпляр (если вы действительно хотите, вы можете отслеживать количество созданных экземпляров в своем конструкторе и вызывать ошибки во время выполнения).
Выбери свой яд 😉