我已经实现了两种方法,我认为应该如何实现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;
}
};
任何意见都将不胜感激,谢谢:)!
答案 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图书馆并让它做适合你的工作。