About Singleton Instances in Qt
-
Hi All,
I've gone through a very good learning exresice last week - the singleton classes. I used it for handling a db instance so that I can use my db anywhere from my program without reinitializing the db. So far my singleton pattern works fine. Take a look at my class skeleton.
@#ifndef DBHANDLER_H
#define DBHANDLER_H
#include <QtSql>class DbHandler {
private:
DbHandler();
DbHandler(const DbHandler&);
DbHandler& operator=(const DbHandler&);
static bool instance;
static DbHandler _dbH;
QSqlDatabase db;
public:
static DbHandler getInstance();
~DbHandler();
void readAllData();
};
#endif@My concerns about this declaration are.
- Is it thread safe?
- Is there any simple way apart from doing all this manually?
I simply understand it is not thread safe. I use a boolean varibale named instance to make sure there exists an instance or not. So another thread can possibly change it. I have gone through the singleton design pattern in Wiki and have seen Java supports "thread safe singleton pattern":http://en.wikipedia.org/wiki/Singleton_pattern#Lazy_initialization. My first question is, does Qt support this? QMutex may be?
Again Java supports far simpler declaration named the "enum way":http://en.wikipedia.org/wiki/Singleton_pattern#The_Enum_way. Does Qt have something similar? Feel free to point out if my assumptions are wrong.Thanks and Regards
-
IMHO, singleton should be avoided as much as possible. That said, I believe you can use QMutex to check if '_dbH' instance is allocated or not in a threadsafe way.
And you also can check Q_GLOBAL_STATIC() macro which can be used for singleto'nize a class' instance easily which is used inside Qt library(though it is not documented, it uses lock-free instance checking routine that is more efficient than using QMutex, i believe) -
Singleton his its uses, but it also has its pitfals. Threading is one such pitfal.
In your implementation, there are some issues:
Why is delete public? Do you really want the users of your API to call:
@
delete DbHandler::getInstance();
@
That would result in a crash the next time you try to access the instance.Also, a Singleton should disable copying of the object, otherwise you can still get more than one instance of the class:
@
DbHandler theCopy(*(DbHandler::getInstance())); // just copied a singleton!
@On the other hand: who is responsible for deleting the instance? One way would be to use a smart pointer instead of the raw pointer. The idea of using a bool too, I don't get.
As you already noticed, there are threading issues. You can jump through fancy hoops to avoid this issue, but the simplest way to avoid issues is just to make sure that before you start your new threads, you initialize the object. You don't really need lazy instantiation for objects that you are sure to use anyway, right? However, do note that that basically reduces your singleton to a glorified global variable.
-
A few annotations:
- the boolean variable instance can be omitted, as the DbHandler pointer to the instance itself can be used to distinct between an initialized (dbH != 0) and a non-initialized (dbH == 0) singleton.
- C++ does support both patterns you've mentioned, thread-safe and lazy-initialized.
@
class ThreadSafeSingleton
{
public:
static ThreadSafeSingleton* instance() { return _instance; }
private:
ThreadSafeSingleton() {}
static ThreadSafeSingleton* _instance;
};ThreadSafeSingleton* ThreadSafeSingleton::_instance = new ThreadSafeSingleton;
class LazySingleton
{
public:
static LazySingleton* instance()
{
if(_instance == 0)
_instance = new LazySingleton;return _instance; }
private:
LazySingleton() {}
static LazySingleton* _instance;
};LazySingleton* LazySingleton::_instance = 0;
@
If the lazy initialized singleton is used in multithreaded applications, instance() has to be made thread-safe (for example using QMutex). To partially negate its cost "double checked locking":http://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf might be used. Alternatively Q_GLOBAL_STATIC() can be used, which is thread-safe as well.- Make sure you've understand the consequences when using a lazy-initialized singleton, especially when used with database connections. Typically a database-driven application establishes the connection to the database on startup once and fails when it isn't able to do so, which is contradictory to the principle of a (lazy-initialized) singleton.
- Make sure you've have another good read on the downsides of singletons in C++, for example undefined construction order, destruction problems or slowed-down startup time.
I agree with joonhwan and Andre that singletons should be avoided. I would even go a step further and would consider them as a classical anti-pattern which should be avoided whenever possible. There is a very good "Tech Talk":http://www.youtube.com/watch?v=-FRm3VPhseI with Miško Hevery explaining most of the issues.
Fortunately Qt requires a comfortable way of having globally accessible data without using singletons: dynamic properties of the application object.
@
class DbHandler
{
// non-singleton, non-thread-safe implementation
// of the database connection and access code
...
};
Q_DECLARE_METATYPE(DbHandler*);int main(int argc, char* argv[])
{
QApplication application(argc, argv);// create an instance of the database in main, where you can // do proper error handling and no thread-safety is required DbHandler* dbHandler = new DbHandler; // Make your DbHandler* globally accessible by settings it as a // dynamic property of the application object. application.setProperty("dbHandler", QVariant::fromValue<DbHandler*>(dbHandler)); ....
}
void SomeObject::someMethod()
{
DbHandler* dbHandler = qApp->property("dbHandler").value<DbHandler*>();
}
@ -
Ahm, am I missing something, or do you wrap a singleton class ([[Doc:QSqlDatabase]]) into a new singleton class? Is that for real code or only for learning purpose?
-
While QSqlDatabase has static methods to get or create instances, it does not qualify as a singleton IMO. It does instance management, but it does not limit you to only one instance.
-
Ok, sort-of singleton :-)
But it serves the purpose very well. You get the same database handle everytime you call the static methods. And of course one will need multiple instances if connections to multiple databases are needed.
But one usually creates a handle, sets it up and opens the connection on applicatin startup. Afterwards one gets the handle via the static method. From a user's point of view, this behaves much like a singleton.
-
Thank you all for your valuable feedbacks.