Singleton类 - 哪种方法

时间:2012-10-29 21:02:28

标签: c++ design-patterns

我已经实现了两种方法,我认为应该如何实现Singleton类,我只想要程序员的意见,哪一种是最好的方法。

每种方法都使用这些类:

class Animal {
public:
    virtual void speak() const = 0;
};

class Dog {
    virtual void speak() { cout << "Woof!!"; }
};

第一种方法:

class AnimalFactory {
public:
    static Animal* CreateInstance(int theTypeOfAnimal);

private:
    AnimalFactory() { };
    static int count; // this will be used to count the number of objects created
    static int maxCount; // this is the max count allowed.
};

int AnimalFactory::count = 0;
int AnimalFactory::maxCount = 1;

Animal* AnimalFactory::CreateInstance(int theTypeOfAnimal)
{
    Animal* pAnimal = NULL;

    if(pAnimal != NULL)
    {
        return pAnimal;
    }

    switch(theTypeOfAnimal)
    {
        case 0:
            pAnimal = new Dog();
            count++;
            break;

        case 1:
            pAnimal = new Cat();
            count++;
            break;

        case 2:
            pAnimal = new Spider();
            count++;
            break;

            default:
            cout << "Not known option";
    }

    return pAnimal;
}

第二种方法:

template<typename classType>
class Singleton {
public:
    classType& instance()
    {
        static classType object;
        return object;
    }
};

任何意见都将不胜感激,谢谢:)!

8 个答案:

答案 0 :(得分:4)

在大多数情况下,Singleton被滥用。一般建议是:不要使用它!如果您认为您确实遇到Singleton是正确解决方案的情况:概率仍然是针对您的。如果你真的,确实需要使用Singleton,你可能仍然希望将它用作某种美化数据:不要。它会伤到你。

好的,你已被警告过了。

第二种方法与C ++ 2011具有明显的优势,我可以安全地使用它。它也恰好更简单。

如果你还没有使用编译器,那么,实现C ++ 2011逻辑并在多线程应用程序中使用Singleton,你需要确保它只被初始化一次,即使访问函数是从两个线程同时调用。当然,这会导致其他不起作用的东西:不要使用双重检查的锁定模式。它[也]不起作用。如果你实现它可以工作,有人会来并“修复”你的代码。

答案 1 :(得分:1)

无论如何,在嵌入式环境中,你不需要单例模式,你甚至不需要new或malloc。

很难解释,但我试试。

嵌入式系统必须正常工作。没有IT部门配备手册,常见问题解答和互联网论坛,没有人会调整你的Linux系统,没有人会修改你的配置,没有人会根据任务的需要安装更大的硬盘或内存。您的程序无法使用有意义的错误代码向stderr和exit()写入详细消息,如果可以的话,它只是意味着您的嵌入式系统无法正常工作。如果您可以在出错时闪烁红色LED,那么它总比没有好,但它仍然意味着:设备失败。

我认为,不使用malloc / free和new / delete 是一种很好的做法。您应该通过将它们定义为静态数组来“预分配”可能的最大对象数。或者,如果最大项目数取决于用户可以更改的配置,则应该在程序启动时分配固定数量的对象,因此启动时会出现内存问题,而不是半小时后。

是的,很多内存都会被浪费,但不会有任何神秘的错误。而且你不必处理程序中的内存问题(仅在启动时)。

答案 2 :(得分:0)

第一种解决方案有一些奇怪的编码(参见Luchian Grigore回答)。忽略: 它是工厂和单身人士之间的一些概念组合。 工厂通常是单身,但它通常发出的不是。

第二个确保您只能创建一个类的一个对象,但仅当您将其包装在Singleton模板中时。如果类具有公共构造函数,则可以在方案外部创建它,从而违反规则。

在我的书中,这些都不是做单身人士的好方法。

进一步扩展:单身人士的想法是因为你需要管理一些特定的东西,你需要一个组件来完全负责。

使用您的第一个版本,您可以拥有多个工厂,每个工厂创建一个动物。在现实世界中没有意义,在代码中没有意义。

你的第二个例子试图允许每个调用(可选)成为一个单例 - 但不是所有类都需要,并且许多不一定在“单例化”时表现。

答案 3 :(得分:0)

看起来你只需要做一个功能就可以省去一大堆头痛:

Animal * CreateAnimal(int theTypeOfAnimal)
{
    static int count = 0;
    static int maxCount = 1;

    switch(theTypeOfAnimal)
    {
        // ...
    }
}

答案 4 :(得分:0)

嗯,第一个不起作用:

Animal* pAnimal = NULL;
if(pAnimal != NULL)
{
    return pAnimal;
}

您是否期望持续的条件?

我之前见过第二个,所以我会继续说它更惯用,至少对于这种类型的单身人士来说,这不是真正的单身人士 - 因为你有多个实例,尽管每个实例类型一个。好吧,一般的想法是可以的(对于单身人士),但我会选择以下的东西:

template<typename classType>
class AnimalManager {
public:
   static Animal& instance()
   {
       static classType object;
       return object;
   }
};

你可以打电话

Animal& animal = AnimalManager<Dog>::instance();
//assuming you derive Dog from Animal, which you're currently not

答案 5 :(得分:0)

我会被大量的Java程序员叮嘱,但我不介意:如果你知道,你的程序只需要一个对象实例,可以随意创建一个全局对象并编写一个静态的get()方法返回(引用)它(并且不从其他地方引用全局对象)。

我认为,Java中的单例模式是一种实现全局对象的技术,在C ++中是一种隐藏全局对象的技术。但是为什么,它隐藏起来就像丑陋一样丑陋,因为它没有隐藏。

您还应该仔细包装它,以使其在场景中显示为其他普通对象,并将其作为普通对象处理。也许,之后,随着程序的发展,会有更多的实例,你会很高兴它已经成为一个普通的对象。

答案 6 :(得分:0)

在回顾中,John Vlisides说,如果一个'模式'不应该进入他的GOF书,那就是单身人士。但是,也请参阅他对implementing it in C++的讨论。

另见Andrei Alexandrescu,explores singletons有3种不同的创作模型,4种不同的终身模型和2种线程模型,这是实现单身的24种方法。

就我个人而言,我只是绕过需要的物体来避开它们。它使测试更容易,界面明确,等等。你已被警告过,当然你已被警告过,但你会听吗?

答案 7 :(得分:0)

第一个代码段无法实现单例,因为AnimalFactory::CreateInstance无法检查它维护的实例计数。

第二个代码段,

template<typename classType>
class Singleton {
public:
    classType& instance()
    {
        static classType object;
        return object;
    }
};

如果做得好,在Scott Meyers之后被称为 Meyers'单身人士

给出的代码可以工作,但会产生尴尬的用法。为避免必须实例化Singleton,应将instance方法声明为static。此外,classType类的要求应该只能由Singleton实例化,例如,作为一个例子的评论。

其他人已经警告过你不要使用单身人士,但我也要这样做。

单身人士有其用途,例如确保Windows API“窗口类”只创建一次,但主要是它们被伪装成全局变量。全局变量的一个问题,即您不知道何时初始化全局变量(在C ++编程中称为静态初始化顺序fiasco ),单个元素可以避免。然而,主要的问题是,他们作为意大利面通信系统,在你永远不会怀疑的地方以无法追踪的方式引导混乱诱导信息,仍然存在。

所以,不要。

但如果你在某个时候发现你绝对需要单身,例如对于日志记录工具,那么请阅读Andrei Alexandrescu的“现代C ++设计”中关于单身人士的讨论,然后抓住 Loki图书馆并让它做适合你的工作。