Singleton mixin C++
My implementation
Mixin from above
错误的实现方式: 未private constructor
/**
* @brief 单例mixin
*
* @tparam DerivedClass
*/
class noncopyable
{
public:
noncopyable() = default;
~noncopyable() = default;
// C++ 11
// =======
// We can use the better technique of deleting the methods
// we don't want.
// Note: Scott Meyers mentions in his Effective Modern
// C++ book, that deleted functions should generally
// be public as it results in better error messages
// due to the compilers behavior to check accessibility
// before deleted status
noncopyable(const noncopyable&) = delete;
noncopyable& operator=(const noncopyable&) = delete;
};
template<typename DerivedClass>
class SingletonMixin: public noncopyable
{
public:
static DerivedClass& GetInstance()
{
static DerivedClass instance;
return instance;
}
};
class Singleton: public SingletonMixin<Singleton>
{
};
int main()
{
Singleton::GetInstance();
Singleton s; // 错误的使用方式也能够编译通过
}
// g++ --std=c++11 test.cpp
通过上述code来看,它无法实现singleton,在main()
中,可以看到Singleton s;
能够编译通过;
错误的实现方式: 在base class中,无法access private constructor
/**
* @brief 单例mixin
*
* @tparam DerivedClass
*/
class noncopyable
{
public:
noncopyable() = default;
~noncopyable() = default;
// C++ 11
// =======
// We can use the better technique of deleting the methods
// we don't want.
// Note: Scott Meyers mentions in his Effective Modern
// C++ book, that deleted functions should generally
// be public as it results in better error messages
// due to the compilers behavior to check accessibility
// before deleted status
noncopyable(const noncopyable&) = delete;
noncopyable& operator=(const noncopyable&) = delete;
};
template<typename DerivedClass>
class SingletonMixin: public noncopyable
{
public:
static DerivedClass& GetInstance()
{
static DerivedClass instance;
return instance;
}
};
class Singleton: public SingletonMixin<Singleton>
{
private:
Singleton(){};
};
int main()
{
Singleton::GetInstance();
Singleton s;
}
// g++ --std=c++11 test.cpp
上述实现方式,将DerivedClass
的constructor声明为private,则上述代码是会编译报错的:
test.cpp: In instantiation of ‘static DerivedClass& SingletonMixin<DerivedClass>::GetInstance() [with DerivedClass = Singleton]’:
test.cpp:26:13: required from here
test.cpp:20:2: error: ‘Singleton::Singleton()’ is private
Singleton()
^
test.cpp:12:23: error: within this context
static DerivedClass instance;
正确的实现方式: CRTP + friend base class
/**
* @brief 单例mixin
*
* @tparam DerivedClass
*/
class noncopyable
{
public:
noncopyable() = default;
~noncopyable() = default;
// C++ 11
// =======
// We can use the better technique of deleting the methods
// we don't want.
// Note: Scott Meyers mentions in his Effective Modern
// C++ book, that deleted functions should generally
// be public as it results in better error messages
// due to the compilers behavior to check accessibility
// before deleted status
noncopyable(const noncopyable&) = delete;
noncopyable& operator=(const noncopyable&) = delete;
};
template<typename DerivedClass>
class SingletonMixin: public noncopyable
{
public:
static DerivedClass& GetInstance()
{
static DerivedClass instance;
return instance;
}
protected:
SingletonMixin()=default;
};
class Singleton: public SingletonMixin<Singleton>
{
public:
friend class SingletonMixin<Singleton>;
private:
Singleton(){};
};
int main()
{
Singleton::GetInstance();
// Singleton s;
}
// g++ --std=c++11 test.cpp
1、在derive class中,通过friend将private constructor开放给base class: 添加上 friend class SingletonMixin<Singleton>;
从而能够消除前面的错误
2、上述friend的用法,是参考自stackexchange Modern C++ Singleton Template。
3、需要将SingletonMixin
的constructor protected
Mixin from below
实现方式一
#include <iostream>
/**
* @brief 单例mixin
*
* @tparam DerivedClass
*/
template<class SingletonClass>
class SingletonMixin: public SingletonClass
{
public:
/** Singleton creation function */
static SingletonClass& GetInstance()
{
static SingletonClass _instance;
return _instance;
}
};
class Singleton
{
public:
void Test()
{
std::cout << this << std::endl;
}
protected:
Singleton()
{
}
};
int main()
{
SingletonMixin<Singleton>::GetInstance().Test();
SingletonMixin<Singleton>::GetInstance().Test();
}
// g++ test.cpp
上述代码编译报错如下:
test.cpp: In instantiation of ‘static SingletonClass& SingletonMixin<SingletonClass>::GetInstance() [with SingletonClass = Singleton]’:
test.cpp:35:29: required from here
test.cpp:28:2: error: ‘Singleton::Singleton()’ is protected
Singleton()
^
test.cpp:15:25: error: within this context
static SingletonClass _instance;
实现方式二
#include <iostream>
/**
* @brief 单例mixin
*
* @tparam DerivedClass
*/
template<class SingletonClass>
class SingletonMixin: public SingletonClass
{
public:
/** Singleton creation function */
static SingletonMixin& GetInstance()
{
static SingletonMixin _instance;
return _instance;
}
};
class Singleton
{
public:
void Test()
{
std::cout << this << std::endl;
}
protected:
Singleton()
{
}
};
int main()
{
SingletonMixin<Singleton>::GetInstance().Test();
SingletonMixin<Singleton>::GetInstance().Test();
}
// g++ test.cpp
能够正常编译通过。
codereview.stackexchange Modern C++ Singleton Template
I recently read about the C++17 static inline
member declaration and thought that this will make templates a little bit cleaner, since static members can now be initialized inside a templated class.
NOTE:
1、对C++17对static inline的总结较好
Here is the template:
template<typename T>
class Singleton
{
public:
static T& GetInstance()
{
static MemGuard g; // clean up on program end
if (!m_instance)
{
m_instance = new T();
}
return *m_instance;
}
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton) = delete;
protected:
Singleton()
{
}
;
virtual ~Singleton()
{
}
private:
inline static T *m_instance = nullptr;
class MemGuard
{
public:
~MemGuard()
{
delete m_instance;
m_instance = nullptr;
}
};
};
class Test final : public Singleton<Test>
{
friend class Singleton<Test> ;
public:
void TestIt()
{
}
;
private:
Test()
{
}
~Test()
{ /* Test intern clean up */
}
};
int main()
{
Test::GetInstance();
}
// g++ --std=c++17 test.cpp -Wall -pedantic
NOTE:
一、实现点:
1、mixin from above + friend base class
2、exit guard
二、上述实现方式,相比于Meyer's singleton太复杂了
三、上述实现方式存在如下问题:
1、race condition
A
Singleton is bad practice
Okay so first of the obligatory Singletons are bad practice so you probably shouldn't make it easy to write bad code
Race condition
Ignoring the fact that the class probably shouldn't exist at all we can look at the code.
static T& GetInstance() {
static MemGuard g; // clean up on program end
if (!m_instance) {
m_instance = new T();
}
return *m_instance;
}
If multiple threads access this instance simultaneously before it is created, you have a data race and m_instance
may end up be being constructed multiple times or other kinds of undefined behaviour. You need to add mutex locks around the if
block or use std::call_once
which is preferred.
As it is supposed to be a singleton you're not supposed to be able to create more instances as the meaning of a singleton is to just have one instance but it appears that it is fully possible to construct multiple instances of Test
simply by creating them as local variables. So this is a design flaw in your template.
Meyer's singleton
A much better way of creating a singleton is to rely on C++11 Magic Statics (N2660). And simply do this:
class Test{
private:
Test(); // Disallow instantiation outside of the class.
public:
Test(const Test&) = delete;
Test& operator=(const Test &) = delete;
Test(Test &&) = delete;
Test & operator=(Test &&) = delete;
static auto& instance(){
static Test test;
return test;
}
};
Which is much easier to write than your code, it's thread safe and fixes the issues with allowing Test
to be instantiated. The properties of magic statics guarantee that test
will be initialised exactly once the first time the function body is entered by any thread, even in the presence of multiple threads that might otherwise cause a data-race. The instance will be deconstructed when your main()
function returns (in the static destruction stage) which makes the whole MemGuard
thing unnecessary.
A
Singleton is bad practice
Singletons make it hard to test your code, and in my job I'd reject this at review for encouraging the development of untestable features. That said, I'll continue reviewing despite that.
No need for helper class
The MemGuard
appears to be a poor man's reimplementation of std::unique_ptr
. It would be much simpler for you to declare m_instance
as a std::unique_ptr<T>
, and then just return *m_instance
from your accessor.
There's a race condition when two or more threads try to create the instance (when both see a null pointer "before" the other has set it). You could work around this with a mutex lock, but it's simpler to use a local static variable, which is thread-safe:
#include <memory>
template<typename T>
T& Singleton<T>::instance()
{
static const std::unique_ptr<T> instance{new T{}};
return *instance;
}
We don't need a destructor
There's no need for the empty virtual destructor, as the constructed object will always be deleted as its declared type.
Revised implementation
NOTE: 实现总结:
1、constructor token
2、mixin from above
With my changes, the code reduces to
template<typename T>
class Singleton {
public:
static T& instance();
Singleton(const Singleton&) = delete;
Singleton& operator= (const Singleton) = delete;
protected:
struct token {};
Singleton() {}
};
#include <memory>
template<typename T>
T& Singleton<T>::instance()
{
static const std::unique_ptr<T> instance{new T{token{}}};
return *instance;
}
I'm using a constructor token to allow the base class to call the subclass's constructor without needing to be a friend
.
Example
An example T
looks like:
#include <iostream>
class Test final : public Singleton<Test>
{
public:
Test(token) { std::cout << "constructed" << std::endl; }
~Test() { std::cout << "destructed" << std::endl; }
void use() const { std::cout << "in use" << std::endl; };
};
Although the constructor is public, it can't be called without a Singleton<T>::token
object, meaning that access to it is now controlled.
Tests:
NOTE:
完整测试程序如下:
#include <memory>
template<typename T>
class Singleton
{
public:
static T& instance();
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton) = delete;
protected:
struct token
{
};
Singleton()
{
}
};
template<typename T>
T& Singleton<T>::instance()
{
static const std::unique_ptr<T> instance { new T { token { } } };
return *instance;
}
#include <iostream>
class Test final : public Singleton<Test>
{
public:
Test(token)
{
std::cout << "constructed" << std::endl;
}
~Test()
{
std::cout << "destructed" << std::endl;
}
void use() const
{
std::cout << "in use" << std::endl;
}
;
};
int main()
{
// Test cannot_create; /* ERROR */
std::cout << "Entering main()" << std::endl;
{
auto const &t = Test::instance();
t.use();
}
{
auto const &t = Test::instance();
t.use();
}
std::cout << "Leaving main()" << std::endl;
}
// g++ --std=c++11 test.cpp
NOTE:
1、输出如下:
Entering main() constructed in use in use Leaving main() destructed
Afterthought:
There's no need for the smart pointer; ordinary memory management works here:
template<typename T>
T& Singleton<T>::instance()
{
static T instance{token{}};
return instance;
}
theimpossiblecode C++11 generic singleton pattern
NOTE: 看了一下,实现非常一般
Using C++11
The solution described here is using lambda expressions and the C++11 thread safety guarantee for function-local statics.
stackoverflow Singleton mixin C++ # A
this should help to solve your problem. Be aware that this singleton is not thread safe. But you can change it if you need to.
See CRTP for more details.
#include <iostream>
#include <map>
#include <string>
// simple singleton
template <class T>
class Singleton {
public:
static T& Instance() { static T instance; return instance; }
protected:
Singleton(){}
};
// your Registry Base
template <class T>
class Registry : public Singleton< Registry<T> > {
friend class Singleton<Registry>;
public:
void register_name( const std::string& name, T value ){ m_data[name] = value; }
const T& lookup_name( const std::string& name ){ return m_data[name]; }
private:
Registry(){}
Registry(const Registry&){} // to prevent copies, you have to use ::Instance()
std::map<std::string, T> m_data;
};
int main(int argc, char *argv[])
{
Registry<int>& instance = Registry<int>::Instance();
instance.register_name("Value1",1);
Registry<int>::Instance().register_name("Value2",2);
int value = instance.lookup_name("Value1");
std::cout << "Value1=" << value << std::endl;
std::cout << "Value2=" << Registry<int>::Instance().lookup_name("Value2") << std::endl;
return 0;
}
mixin from above + friend base class
chromium singleton
source code: https://github.com/chromium/chromium/blob/master/base/memory/singleton.h
这个实现非常复杂