Solved question about singletons
-
Hi all -
I know this probably isn't Qt-specific, but...I've started to implement my various models as singleton objects. I found this old post that explains the step I missed, but, can someone explain to me why I need to include this line in my source code:
EquipmentModel *EquipmentModel::m_instance = nullptr; // for singleton.
I don't understand why the (static) declaration in the class isn't sufficient.
Thanks...
-
@mzimmers said in question about singletons:
.I've started to implement my various models as singleton objects
Don't do this.
I don't understand why the (static) declaration in the class isn't sufficient.
Because you also need a definition of your static variable (== the memory)
-
@Christian-Ehrlicher said in question about singletons:
@mzimmers said in question about singletons:
.I've started to implement my various models as singleton objects
Don't do this.
OK. What do you recommend instead to ensure there will be just one instance of my model?
I don't understand why the (static) declaration in the class isn't sufficient.
Because you also need a definition of your static variable (== the memory)
So declaring a member to be static doesn't actually create the member; it just says when one is created, it's the only one? Is that it?
Thanks...
-
@mzimmers said in question about singletons:
OK. What do you recommend instead to ensure there will be just one instance of my model?
Create only one model in your main object.
Is that it?
Yes
-
@Christian-Ehrlicher OK, I'll take your word for it. Can you give me a brief explanation why singletons are to be avoided in this use case?
Thanks...
-
Yeah, what he's getting at is that the singleton design pattern is the most overused pattern in the industry. It's like a new toy and folks just go nuts with it when it really isn't necessary. There are certainly real cases where it is important, but make sure it really is important because it needlessly complicates your code if it really doesn't need to be a singleton class.
As you probly already figured out, the singleton implementation in C++ is to have a private constructor and use a static member Create() function that tests and sets a pointer to a single instance of the class, and fails if it's already been instantiated.
Sometimes people make the mistake of using a singleton when what they really want is to guarantee only a single running instance of the whole program. I always liked the netsocket bind solution to that problem. Since only a single process can bind to a socket, trying to run another instance of the program fails as long as the socket is held by the previous. This is also the most common way to accomplish in JAVA since it is so blasted hard to do anything system/OS related under JAVA.
-
The others are of course right, but Singletons do have their applications, especially in Qt. As they are one of only 3(ish) ways to share/access data between/from QML and c++
-
Well, if you really do want to use singletons, make sure to use a proper, thread-safe approach in C++. Namely, use a static variable inside a function:
class MyClass { //... static MyClass &instance() { static MyClass object; return object; } };
static variables inside a function are automatically thread-safe (since C++11). A class variable is not automatically thread-safe.
The best explanation I have heard against using singletons is because of testing. There is a good talk about singletons and what to use instead: https://www.youtube.com/watch?v=K5c7uvWe_hw
-
@J-Hilk said in question about singletons:
The others are of course right, but Singletons do have their applications, especially in Qt. As they are one of only 3(ish) ways to share/access data between/from QML and c++
QML singleton isn't the same as C++ singleton, and there are intricacies with that integration, true. However in the usual case the C++ object is managed by the QML engine, which will free it whenever it tears itself down, which is the correct way.
@SimonSchroeder said in question about singletons:
static variables inside a function are automatically thread-safe (since C++11). A class variable is not automatically thread-safe.
No they aren't. Only the initialization is thread-safe, which is also guaranteed for any global static (scoped or not). This is not the main problem, though. You could've made the init thread safe manually, if necessary.
@mzimmers said in question about singletons:
Can you give me a brief explanation why singletons are to be avoided in this use case?
Multiple reasons. For one they are evil coupling, way against OOP principles, where you want to decouple, not vice versa.
Secondly, they have lifetime problems. If you do it on the heap (as you seem to have opted for) who cleans up after you're done? In Qt theQCoreApplication
is already a singleton, why would you need more. You can attach any QObject to it at any time you want. Moreover, you shan't create anyQObject
before theQCoreApplication
is live, so you're going to get into real trouble if you naively do:MyClassFromQObject m_instance; int main() { .... }
This violates the API rules, and may/will crash cryptically, which is worse.
Just do yourself a favor and don't do it!
-
@kshegunov said in question about singletons:
QML singleton isn't the same as C++ singleton, and there are intricacies with that integration, true. However in the usual case the C++ object is managed by the QML engine, which will free it whenever it tears itself down, which is the correct way.
I'm not talking about QML singletons
I'm talking about this one:
https://doc.qt.io/qt-6/qqmlengine.html#qmlRegisterSingletonInstance -
@J-Hilk said in question about singletons:
This is a QML singleton. Notice that you inject the object pointer to the QML engine. Your C++ object is created at runtime somewhere, presumably as a
main()
local or a child of some otherQObject
. -
but pre 5.14 you were required to pass on a function pointer, static getter, instead of the object pointer, IIRC
-
@kshegunov said in question about singletons:
In Qt the QCoreApplication is already a singleton, why would you need more
This is the key point to me. "Singletons are evil", but why not hitch a ride on our application object and put everything you want into it? Then you don't create any singletons, but you still do singleton stuff even if you're not aware of it....
-
@JonB said in question about singletons:
This is the key point to me. "Singletons are evil", but why not hitch a ride on our application object and put everything you want into it?
I didn't say to put anything into it. You should not derive from the application class without a very, very, very good reason.
What you should do is compose the objects, where you can use the application object as root/parent.Then you don't create any singletons, but you still do singleton stuff even if you're not aware of it....
The app object is a pseudo-singleton. It is accessible from everywhere, but it is created and initialized in
main()
, so it's not a global by itself. Only the reference to it is made global. Qt has its fair share of global variables, but they're not exposed explicitly to the user, with a good reason, they are a detail that shouldn't interest you. -
@kshegunov said in question about singletons:
What you should do is compose the objects, where you can use the application object as root/parent.
...and thereby take advantage of the "singleton-ness" without you craeting your own singleton... Maybe that's OK, I don't know. If you only add objects with application as parent I don't know how you access them.
The app object is a pseudo-singleton. It is accessible from everywhere, but it is created and initialized in main(), so it's not a global by itself. Only the reference to it is made global.
This is interesting. I'm not sure now what exactly it is that is objectionable/to be avoided in singletons. You still have the
static QCoreApplication::instance()
paradigm. You seem to be saying something about things are OK because it's created inmain()
, what you don't like is it being created outside main, or something?I know it's a tricky area, but I don't think I'm the only one who does not understand what exactly is "bad" versus "ok" when people talk about don't use singletons.
While we are on this: the whole way Qt does
QSqlDatabase::addDatabase()
. Such that you can then access your database from anywhere. Somewhere along the line it must hold a single "list" of these (maybe it's private in the application object, I don't know). And that list is my idea of some kind of singleton. -
@JonB said in question about singletons:
...and thereby take advantage of the "singleton-ness" without you craeting your own singleton... Maybe that's OK, I don't know. If you only add objects with application as parent I don't know how you access them.
You don't. Not directly anyway. You connect their signals and their slots and leave them in the wild. ;)
This is interesting. I'm not sure now what exactly it is that is objectionable/to be avoided in singletons. You still have the
static QCoreApplication::instance()
paradigm. You seem to be saying something about things are OK because it's created inmain()
, what you don't like is it being created outside main, or something?It's marginally better if it's created in
main()
, because you guarantee the creation order (being explicit), which isn't true for simple globals. It's still coupling, but there's no one glove that fits all. A local static you could use to have init on use, but then you may run into trouble if it's accessed while global inits run (e.g. if the local static depends on a global static you're back in UB land).I know it's a tricky area, but I don't think I'm the only one who does not understand what exactly is "bad" versus "ok" when people talk about don't use singletons.
The singleton is a glorified global variable, so if you're okay with having a thing in a global and bite the bullet, then you can use it. If there's a better way than having a global variable (which is 99% of cases), then don't use it. It is that simple.
While we are on this: the whole way Qt does
QSqlDatabase::addDatabase()
. Such that you can then access your database from anywhere. Somewhere along the line it must hold a single "list" of these (maybe it's private in the application object, I don't know). And that list is my idea of some kind of singleton.It's a global variable (a hash map) that's buried within the implementation. The interface to access it however does more than just return a reference to it - it makes sure that you can call it safely from different threads for example.
-
-
@kshegunov said in question about singletons:
static variables inside a function are automatically thread-safe (since C++11). A class variable is not automatically thread-safe.
No they aren't. Only the initialization is thread-safe, which is also guaranteed for any global static (scoped or not).
Thank you for clarifying this. It is what I meant (but not what I wrote).