我犯的错误:有关singleton
错误想法:
在我的ClassFactory的代码当中,用来实现singleton的代码是这样的:
1 | #ifndef MQPOOL_INCLUDE_CLASS_FACTORY_H_ |
因为我看《Effective C++》的条款4的实现:
1 | class FileSystem { ... } |
使用static
的local-static
变量,只有在Instance()
函数被调用的时候才会创建,从而有了延迟初始化的效果,提高了程序的启动性能。
并且Meyers还说了:”这样的单纯性使它们成为绝佳的inlining候选人.尤其是如果它们被频繁调用的话”,所以想当然的,我的实现就成了上面的那个样子。但其实大错特错。
构造函数不应该为public
错误的原因就是构造函数不应该为public
,将一个要作为singleton
的对象的构造函数声明为public
的话,那就意味着用户的代码能够随意构建一个ClassFactory
类型的对象,这就违背了singleton
的单一对象原则。特意将static
对象返回的目的—-达到实例有且仅有一个的要求,就变得没有任何意义。这样就违反了条款18:让接口容易被正确使用,不易被误用。
那么应该选择protected
还是private
呢?
我觉得为了继承的考虑,应该选择protected
,因为如果将构造函数声明为private
,那么这个ClassFactory
的派生类当中的函数也将不能访问这个构造函数,那么当需要构造一个派生类的对象的时候,将不能成功,因为不能调用基类的构造函数,这对派生类的使用场景有很大的限制。
以下是我的实现:
1 | #ifndef MQUEUE_INCLUDE_CLASS_FACTORY_H_ |
1 | ClassFactory& ClassFactory::Instance() { |
完整代码在这里:https://github.com/adairjun/MQueue/blob/master/include/MQueue/class_factory.h
non-member function
的singleton
首先继续明确这一点:就是我的inline
的non-member function
形式的singleton
错误的根本在什么地方。上面也说了,是因为构造函数是public
的情况,但是考虑这样的一种情况,如果我的singleton
对象并不是我自己定义的类,而是STL
当中的呢?比如说一个map
,这个时候我们对于不是自己能够掌控的对象当然是不能把它的构造函数放在protected
当中去了。
所以我只能这样做:
1 | typedef std::map<std::string, int> KeyValue; |
这样一来,这个singleton
那就不应该开放给用户,只是用在自己的代码里面,而且应该把它的使用场景严格限制,比如说把它封装在一两个函数当中,以后不显示调用它,而是通过函数来使用它。
比如:
1 | inline void AddToKeyValue(std::string key, int value) |
我的意思是以后仅仅只通过AddToKeyValue
和GetKeyValue
这两个函数来调用GlobalKeyValue()
,除此之外绝对不在代码中出现GlobalKeyValue()
。
继续讨论一下singleton
的重用方案。
其实要把这个ClassFactory
标记为singleton
并不需要直接把singleton
的代码写进去,只要自己实现一个singleton
的抽象类,然后让ClassFactory
继承它就可以了,以后只要用到singleton
的地方只需要继承这个抽象类就可以了。
可以直接用模板写成这种形式:
1 | template<typename T> |
那么我使用ClassFactory
的方法就是:
1 | class ClassFactory : public Object, public Singleton<ClassFactory> { |
这样一来我的代码就变成了多重继承,而Meyers在《Effective C++》的条款40:“明智而审慎地使用多重继承”。侯捷老师的翻译很贴切,我觉得Meyers每次说道“明智而审慎地使用”,他的意思就是能不用就不用。我还是按照他说的去做吧,在这里我仅仅只是做个实现,真正要用的话,我还是不会去使用多重继承的。