【发布时间】:2015-01-11 15:54:16
【问题描述】:
我正在用 C++ 编写一些类来充当个人小型库,但遇到了问题。
我的一些对象使我们成为以华丽的 C 风格编写的第三方库。
这意味着这些库具有 apiInit() 和 apiCleanup() 之类的函数,其中前者必须在任何实际 api 函数之前调用,而后者必须在您不再使用它们时调用。
我想要的是为需要库的类提供对其函数的访问点,以确保在创建第一个需要的类时或至少在使用任何 api 函数之前调用 apiInit(),并且 apiCleanup()当使用 api 的最后一个实例被销毁时调用。
请记住,有多个类使用单个库。
我可以提供两种解决方案:
首先,很明显,将提供者设为单例:
#include <iostream>
using namespace std;
class ContextProvider {
ContextProvider() {
cout << "Initializing API" << endl;
}
ContextProvider(ContextProvider const& rhs) = delete;
ContextProvider& operator=(ContextProvider const& rhs) = delete;
public:
~ContextProvider() {
cout << "Cleaning API" << endl;
}
static ContextProvider& getInstance() {
static ContextProvider instance;
return instance;
}
void useContext() {
cout << "Using API" << endl;
}
};
class ContextUser1 {
public:
ContextUser1() {
}
void doSomething() {
ContextProvider::getInstance().useContext();
}
};
class ContextUser2 {
public:
ContextUser2() {
}
void doSomethingElse() {
ContextProvider::getInstance().useContext();
}
};
另一种方法是保留上下文用户的计数器,如下所示:
#include <iostream>
using namespace std;
class ContextProvider {
static unsigned int userCounter;
public:
ContextProvider() {
if (userCounter == 0)
cout << "Initializing API" << endl;
++userCounter;
}
~ContextProvider() {
--userCounter;
if (userCounter == 0)
cout << "Cleaning API" << endl;
}
void useContext() {
cout << "Using API" << endl;
}
};
unsigned int ContextProvider::userCounter = 0;
class ContextUser1 {
ContextProvider cp;
public:
ContextUser1() {
cp = ContextProvider();
}
void doSomething() {
cp.useContext();
}
};
class ContextUser2 {
ContextProvider cp;
public:
ContextUser2() {
cp = ContextProvider();
}
void doSomethingElse() {
cp.useContext();
}
};
int main() {
ContextUser1 cu11, cu12, cu13;
ContextUser2 cu21, cu22;
cu11.doSomething();
cu12.doSomething();
cu21.doSomethingElse();
cu22.doSomethingElse();
cu13.doSomething();
}
两者,当使用以下 main() 执行时
int main() {
ContextUser1 cu11, cu12, cu13;
ContextUser2 cu21, cu22;
cu11.doSomething();
cu12.doSomething();
cu21.doSomethingElse();
cu22.doSomethingElse();
cu13.doSomething();
}
yield 预期的结果,即:
Initializing API
Using API
Using API
Using API
Using API
Using API
Cleaning API
现在显而易见的问题是,哪个更好,或者我应该使用哪个?
例如,想到的一些事情是……
单例方法:
-
优点:
- 无需存储任何计数器。
- 无需存储任何实例。
-
缺点:
- 语法变得奇怪 (
ContextProvider::getInstance().use())。 - 这是一个单例,有所有缺陷。
- 语法变得奇怪 (
计数器方法:
-
优点:
- 用法很简单。
- 语法简洁明了 (
cp.use())。
-
缺点:
- 必须保留用户数量的计数器。
- 用户类必须存储 ContextProvider 类的实例。
我主要问这个问题是因为我不知道这些优点/缺点中哪一个更重要,如果有我没有考虑到的事情,或者可能有我无法想出的明显的第三种方法天生就比那两个好,或者,谁知道呢。
感谢您的宝贵时间!
【问题讨论】:
-
您可能正在寻找所谓的"nifty counters"。这是标准库用来确保
std::cout等在首次使用之前已初始化且可用的技巧。 -
重新启动 API 的成本是多少? API all 的对象多久会被销毁,然后很快就会创建新的对象?
-
@IgorTandetnik 这不正是我在第二个示例中所做的吗?
-
@rpattiso 是的,我想过这个。我不知道它有多贵,但我正在考虑像 GLFW 或 WinSock 这样的 API,所以这种情况不应该发生,或者至少非常零星发生。
-
也许code review 更适合这个。
标签: c++ api oop design-patterns singleton