这个Singleton替代品好吗?

时间:2013-12-16 15:05:09

标签: c++ singleton

我正在为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所指出的,我当前的新解决方案是每次访问控制台时重置库的全局设置,而不是一次限制控制台实例的数量。还有其他我应该注意的选择吗?

3 个答案:

答案 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个实例,这是一个简单的封装问题。