我正在为GNU readline创建一个简单的C ++包装器,称为Console。 GNU库位于C中,并保持执行各种任务的当前已注册函数的全局状态。例如,它允许注册执行自动完成和其他任务的全局函数。
同时,我希望这个类维护一个本地状态,包含例如一些注册命令,当前历史记录等,这些在Console实例之间应该是不同的。鉴于我只允许下面的单个全局状态,我只能在任何给定时间允许一个控制台。
但是,使控制台成为单身人士意味着全局可访问性。因此,如果代码的一部分使用具有某些设置的控制台,则它将无法假设它为控制台设置的设置和状态将保持不变,因为代码的任何其他部分将可以访问Singleton控制台。
由于我不喜欢这样,因为我希望将代码管理特定的I / O放在一起,我认为让这个类成为Singleton并不是一个好主意。我最初的解决方案是:
class Console {
public:
Console() {
if ( instanced_ ) throw std::runtime_error("Console already instanced");
instanced_ = true;
}
static bool exists() { return instanced_; }
private:
static bool instanced_ /* = false */;
}
正如本answer所指出的,我当前的新解决方案是每次访问控制台时重置库的全局设置,而不是一次限制控制台实例的数量。还有其他我应该注意的选择吗?
答案 0 :(得分:2)
抛出异常似乎有点苛刻。为什么没有私有构造函数并在第一次请求时初始化Console
,然后返回该实例。
答案 1 :(得分:1)
您可以拥有包含static
C接口的私有readline
成员,并使您的类的所有实例与该单个静态实例协调。如果你认为你的类只是readline
的方法包装并且在类中没有状态,那么就没有理由让类本身成为一个单例。
也就是说,如果您的程序有两个部分,请执行以下操作:
Console fred;
string input = fred.readline();
和
Console barney;
string input = barney.readline();
只要Console
本身没有非静态状态,就没有真正的伤害。另一种方法是传递一个Console
实例,或者有一个返回该实例的全局函数,但它什么也得不到。
事实上,readline
本身会迫使你进入一个由每个人共享的全局状态的单个实例,而你只是在它周围放置一个对象包装器。
编辑:从您更新的问题来看,这听起来像您真正想要的是一种互斥形式,其中只有一个当前活跃的Console
客户端一段时间,而不是“在整个计划生命周期中只有一个Console
。”
你可以考虑做的一件事是:建立你的班级,混合使用static
和非static
状态。 static
州会跟踪与Console
互动的libreadline
的最后一个实例。
然后,在需要与libreadline
交互的每个方法中,您可以调用一个私有方法来确保此特定Console
实例的状态已交换在电话会议之前进入libreadline
。
我建议这样的事情:
class Console
{
static Console *current;
void make_current(void)
{
if (current == this)
return;
if (current)
current->save_state();
// copy our state into libreadline here, and then make us current
...
current = this;
}
void save_state()
{
// copy our state out of libreadline here, and then relinquish current
...
current = nullptr;
}
public:
void method_foo() // this method interacts with libreadline
{
make_current();
// ...
}
void method_bar() // this method does not interact with libreadline
{ // no calls to make_current required
}
~Console()
{
// If we're current when we're being destroyed, relinquish
// current. We don't need to save anything, as we're going away.
if (current == this)
current = nullptr;
}
};
static Console::current = nullptr; // make sure current initializes to nullptr
答案 2 :(得分:0)
也许我应该多说一下这堂课,尽管我只是 寻找一种实例化它的好方法。它有一个州,和 客户端可以注册也自动完成的命令。如果我 创建一个新实例,例如,它将没有命令,但是 默认的。此外,在施工期间,客户也可以设置 一堆图书馆的选择。
使用单例意味着新用户必须先行 删除所有现有命令,但后来无法知道是否有其他人 仍在使用它。因此我想避免这种情况。
这种描述让我相信你过分思考这个问题。如果每个用户都拥有自己的Console
实例,那么您可以通过在{{1}中拥有Console
成员变量将User
的单个实例绑定到Console
} .class。如果只有1个类型的成员变量,那么每个用户只能拥有一个实例。
您的初始描述表明您希望程序生命周期中有一个实例,这会导致您使用Singleton模式(就像它所做的那样)。您希望每个用户拥有1个实例,这是一个简单的封装问题。