如果未调用某些成员函数,是否可能生成编译时错误
Is it possible to generate a compile-time error if some member function hasn't been called
我正在用单例模式编写 class。
class GroupListDAO {
public:
static GroupListDAO* getInstance() {
static GroupListDAO groupListDAO;
return &groupListDAO;
}
init(server::mysqldb::MysqlHelperTempalte* pHelper) {
mysqlHT = pHelper;
}
bool getUserHeartNum(uint32_t owner, uint32_t& totalNum);
bool setUserHeartNum(uint32_t owner, uint32_t totalNum, uint32_t update_time);
private:
MysqlHelperTempalte *mysqlHT;
GroupListDAO() = default;
~GroupListDAO() = default;
};
如您所见,此 class 用于连接 Mysql。所以成员数据mysqlHT
必须在调用任何其他成员函数之前初始化。
总之,class用户必须使用这个class如下:
GroupListDAO *p = GroupListDAO::getInstance();
p->init(XXX); // init must be called before calling any other member functions
p->getUserHeartNum(...);
p->setUserHeartNum(...);
所以我在想是否有办法强制 class 用户调用函数 init
。这意味着如果 class 用户代码是这样的:
GroupListDAO *p = GroupListDAO::getInstance();
p->getUserHeartNum(...);
p->setUserHeartNum(...);
可能会产生一些编译时错误。
Ofc,你可能会说我们可以在其他成员函数中if (mysqlHT == nullptr) { throw exception; }
,但那将是运行时错误,而不是编译时错误。
真实案例
一个大项目由5个开发人员开发。他们都在使用一些单例对象。但他们必须说:嘿,我已经初始化了,你们可以在代码中使用它。或者,不好意思,我们都忘记初始化了...
如评论中所建议,您可以使用默认参数:
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 &groupListDAO;
}
};
GroupListDAO
可能需要是实际对象的包装器,并且在使用 nullptr
调用时可以抛出其构造函数。我不知道创建编译器错误的简单方法。
PS:其实我觉得你的需求是矛盾的。一方面,您需要一个对调用者隐藏初始化的单例。可以通过 getInstance
获取实例,无需关心初始化。另一方面,您确实想关心初始化,因为对 getInstance
的第一次调用是“特殊的”。单例模式和使用 init
方法都不是孤立的完美习语。当一起使用时,它们会更糟。我并不是说这是绝对不行的,但是在应该保留两种方法的情况下必须做出妥协也就不足为奇了。
如果你真的想要一个编译错误,你将不得不摆脱你的单例模式:
class GroupListDao
{
public:
GroupListDao(...) { /* do your initialization here */ }
bool getUserHeartNum(uint32_t owner, uint32_t& totalNum);
bool setUserHeartNum(uint32_t owner, uint32_t totalNum, uint32_t update_time);
// ...
}
这样可以确保 GroupListDao
的任何实例都已初始化。然后您将不得不在您的代码中传递这个实例。因此,您无法再确定在编译时您的应用程序中只有一个 GroupListDao
实例(如果您真的想要,您可以在构造函数中跟踪创建实例的数量并在运行时引发错误)。
选择你的毒药 ;)
我正在用单例模式编写 class。
class GroupListDAO {
public:
static GroupListDAO* getInstance() {
static GroupListDAO groupListDAO;
return &groupListDAO;
}
init(server::mysqldb::MysqlHelperTempalte* pHelper) {
mysqlHT = pHelper;
}
bool getUserHeartNum(uint32_t owner, uint32_t& totalNum);
bool setUserHeartNum(uint32_t owner, uint32_t totalNum, uint32_t update_time);
private:
MysqlHelperTempalte *mysqlHT;
GroupListDAO() = default;
~GroupListDAO() = default;
};
如您所见,此 class 用于连接 Mysql。所以成员数据mysqlHT
必须在调用任何其他成员函数之前初始化。
总之,class用户必须使用这个class如下:
GroupListDAO *p = GroupListDAO::getInstance();
p->init(XXX); // init must be called before calling any other member functions
p->getUserHeartNum(...);
p->setUserHeartNum(...);
所以我在想是否有办法强制 class 用户调用函数 init
。这意味着如果 class 用户代码是这样的:
GroupListDAO *p = GroupListDAO::getInstance();
p->getUserHeartNum(...);
p->setUserHeartNum(...);
可能会产生一些编译时错误。
Ofc,你可能会说我们可以在其他成员函数中if (mysqlHT == nullptr) { throw exception; }
,但那将是运行时错误,而不是编译时错误。
真实案例
一个大项目由5个开发人员开发。他们都在使用一些单例对象。但他们必须说:嘿,我已经初始化了,你们可以在代码中使用它。或者,不好意思,我们都忘记初始化了...
如评论中所建议,您可以使用默认参数:
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 &groupListDAO;
}
};
GroupListDAO
可能需要是实际对象的包装器,并且在使用 nullptr
调用时可以抛出其构造函数。我不知道创建编译器错误的简单方法。
PS:其实我觉得你的需求是矛盾的。一方面,您需要一个对调用者隐藏初始化的单例。可以通过 getInstance
获取实例,无需关心初始化。另一方面,您确实想关心初始化,因为对 getInstance
的第一次调用是“特殊的”。单例模式和使用 init
方法都不是孤立的完美习语。当一起使用时,它们会更糟。我并不是说这是绝对不行的,但是在应该保留两种方法的情况下必须做出妥协也就不足为奇了。
如果你真的想要一个编译错误,你将不得不摆脱你的单例模式:
class GroupListDao
{
public:
GroupListDao(...) { /* do your initialization here */ }
bool getUserHeartNum(uint32_t owner, uint32_t& totalNum);
bool setUserHeartNum(uint32_t owner, uint32_t totalNum, uint32_t update_time);
// ...
}
这样可以确保 GroupListDao
的任何实例都已初始化。然后您将不得不在您的代码中传递这个实例。因此,您无法再确定在编译时您的应用程序中只有一个 GroupListDao
实例(如果您真的想要,您可以在构造函数中跟踪创建实例的数量并在运行时引发错误)。
选择你的毒药 ;)