Можно ли сгенерировать ошибку во время компиляции, если не была вызвана какая-либо функция-член

#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 во время компиляции в вашем приложении есть только один экземпляр (если вы действительно хотите, вы можете отслеживать количество созданных экземпляров в своем конструкторе и вызывать ошибки во время выполнения).
Выбери свой яд 😉