45

Let say we need to have just one instance of some class in our project. There are couple ways of doing it.

I want to compare. Please can you review my understanding.

1) Classical Singleton pattern

2) Completely static class (all methods and members are static).


As I understand the differences are following:

a) The order of initialization of static members across different units isn't defined. So, completely static members initialization can't use any static members/functions from other modules. And singleton doesn't have this problem.

b) We have to deal with threading for getInstance() of Singleton. However, completely static class doesn't have this problem.

c) Access to methods looks a little bit different. Foo::bar(); vs Foo::getInstance()->bar(); Generally, singleton can return NULL to identify that there were some problems with construction of object and static class can't.

d) Definition of class looks a little bit clunky with bunch of statics for static class.

Have I missed anything?

SCFrench
  • 8,244
  • 2
  • 31
  • 61
Victor Ronin
  • 1,101
  • 2
  • 9
  • 5
  • 24
    Then there's option **(3) Just create one instance of the object.** Really, there's not more to it than that. If you only need one instance, why is it so difficult to create only one instance? Do you typically create more objects than you need when you write code in C++? – James McNellis Oct 01 '10 at 16:16
  • possible duplicate of [C++ singleton vs. global static object](http://stackoverflow.com/questions/1463707/c-singleton-vs-global-static-object) – Klaim Oct 01 '10 at 16:19
  • Will a Singleton ever return different values for consecutive calls to `getInstance()`? If not, then concern (c) is moot since a call doesn't look like `Foo::getInstance()->bar();`; it looks like a single `Foo* foo(Foo::getInstance());` at the start of your block and `foo->bar();` thereafter. – Mike DeSimone Oct 01 '10 at 16:20
  • 1
    @James McNellis - If he really has a threading problem with `getinstance()`, then he'd have the same problem with synchronizing the creation of his one instance. And where do you put the one instance, if there's no logical owner? You could create one, but if you do that *and* solve the creation threading issue, you've basically rolled a singleton. – T.E.D. Oct 01 '10 at 16:21
  • you may also want to refer to http://stackoverflow.com/questions/887317/monostate-vs-singleton – Chubsdad Oct 01 '10 at 16:24
  • 2
    @T.E.D. you missed the point: James was saying that there was no need to try and enforce the unicity via some some pattern when you could just create one object somewhere without enforcing anything (let's say, in `main`, before threading) and be done with it. – Matthieu M. Oct 01 '10 at 16:24
  • @Matthieu M.: I agree. For an example, see my response here: http://stackoverflow.com/questions/3789280/destroying-a-singleton-object/3789659#3789659 – Steve M Oct 01 '10 at 16:27
  • @Klaim - I can't decide if this is really a dup of that question, but Victor should definitely read that other question and its answers. – T.E.D. Oct 01 '10 at 16:30
  • @Matthieu M. - I think you are missing mine. I suppose he (and you) don't have to **call** it a singleton if it bothers you, but if he has code to synchronize the creation of his one instance between his multiple threads, and he creates a special class or namespace just to put it in (since it doesn't really belong to anyone), and perhaps even some more code to prevent accidentally constructing new copies (since he wants everyone using the same one of them), then that's essentially what he's created. – T.E.D. Oct 01 '10 at 16:40
  • @TED: I don't call an object in main a Singleton because for testing purposes I could bring a new one to life for each of my different test cases (to insulate them for one another). I would therefore rely on "guideline" rather than "code", it may appear clunky but it works really well. – Matthieu M. Oct 01 '10 at 16:45
  • I just want to chime in to give an example where a fully static class might be the preferred choice: let's say I do not want to have any memory allocation (ex: embedded case), to ensure that I can avoid risks later on to run out of memory. Then I am left with 2 options: a) declare a variable and b) make a fully static class. The latter would seem more elegant and probably use less memory. – Igor Stoppa Jun 20 '16 at 20:25
  • Not a c++ guru so hopefully someone can correct me if I'm wrong, but passing an instance method as a callback function is non-trivial because an instance method has as an implicit parameter the object that it's invoked on, which would not be available to the calling function. This is not a problem with a static class. – farhadf Jul 27 '18 at 01:01

5 Answers5

18

Whether you call it Singleton or Monostate or any fancy name... the very annoying nature of this is that you have ONE instance of the object and many writes to it: global variables, whatever their guise, are evil.

The idea that you need a unique instance is generally clunky. Most of the times what you really need is parts that communicate share the same instance. But another group of parts could perfectly use another instance without issue.

Any code that claim to need a global variable is highly suspicious. It may appear simpler to use one, but let's face it, you could perfectly pass the object to each and every function, it would complicate their signature but it would work nonetheless.

However, I admit, it appears simpler to use global variables... until you notice the issues:

  • multithreading is compromised
  • testability is reduced, since one test may affect the one following it
  • dependency analysis is extremely complicated: it's hard to know what state your method depend on when you pull in global from within submethods...

Now, as far as singleton is concerned, multithreaded creation is not usable in C++ before C++0x (when it becomes possible using static locals), thus you need to create it in only one thread and delay access before: instantiate it in main, it's your best bet.

Destruction may cause mayhem since the life of the Singleton / Static may end before others are done with it, and then it's undefined behavior. This is typical of a Logger singleton. The usual strategy is to shamelessly leak...

After that, if you still want one, I wish you good luck, that's all this community can do for you.

Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
  • 1
    Lol. Even though we are arguing in the comments, +1 on this. Agree totally. – T.E.D. Oct 01 '10 at 16:44
  • +1: Your response is better than mine, so I'm deleting mine. – John Dibling Oct 01 '10 at 16:49
  • 26
    DO you use std::cout/std::cerr or do you pass an iostream to every function/object in case it needs to report an error? – Martin Beckett Oct 01 '10 at 16:57
  • @Martin: I have a DiagnosticHandler class that I use; I instantiate a single instance of it and pass a reference or smart pointer to it (depending on the application) to any object that needs it. It may use `std::cout` to emit errors (it's highly configurable), but the use of that global is hidden from application code. It works very well and in my opinion is much, much cleaner than using a global variable. – James McNellis Oct 01 '10 at 17:15
  • 2
    @Martin Becket: for my pet projects, sure I do (during debug), otherwise I prefer references because I can simply redirect the output to files instead. At work, we use a logging library which sends the messages to another server and probably relies on a singleton somewhere... but I don't know (or care) because I am not maintaining it. – Matthieu M. Oct 01 '10 at 18:18
  • I don't really understand your comment on re-entrance. If your plan is to operate on a global object because the functions _rely_ on the communication, then passing the variable around by pointer or reference doesn't make your code any more reentrant. I certainly agree with the main thrust of your post, though. – San Jacinto Oct 01 '10 at 19:39
  • @San Jacinto: if you have a global variable, it may get polluted by a previous work. Now you set part of it, and read a bit more than what was set... bam you're reading data that were set by a previous work, but it still works... till you change the flow a bit, and that leaves your very perplex. If you need communication, then you can instantiate the 'Context' object at the beginning of your work, and destroy it at the end, insulating this process from the rest of the code. That's how you gain re-entrance, and easier diagnosis. – Matthieu M. Oct 02 '10 at 17:54
  • @Matthieu We might be facing a language barrier here, but reentrance means a single procedure does not modify shared data. I agree that your solution is better, so don't misunderstand me... but it does not solve the reentrance problem in that if it's shared data (i.e. data that MUST be shared, the only legitimate reason for even considering a global datum), it doesn't matter whether it's global or passed in to a function via pointer or reference. At that point, the function is no longer reentrant. If the current function is, the one above it isn't b/c it needs to block for the shared object. – San Jacinto Oct 02 '10 at 21:55
  • @San Jacinto: I know what reentrance mean... and I am afraid my mind slipped here and I wasn't attentive enough to your explanations. Thanks for insisting :) – Matthieu M. Oct 03 '10 at 14:12
  • All the issues you mentioned are can be viewed as problems with either bad tests or bad code. Regardless of what you use to get your "one" instance of an object. – iheanyi Jun 12 '14 at 17:38
  • I don't understand all these strong feelings around singletons. There are perfectly valid cases for using singletons, and there is no reason to try to stay away from them. For example, a computer's main display is one physical object, which is conveniently modeled using a singleton, on which various view's can be displayed. There are reentrant functions that governs this, maintaining coherence internally. You would not want 100 objects all trying to manage the main display, not knowing each other's states. You would also not want 100 objects all communicating back and forth for that. – Laine Mikael Nov 30 '15 at 00:09
  • @LaineMikael: There are TWO issues with a Singleton: (1) Single and (2) ton Global. Global variables are bad for a lot of reasons, and while it can be seen as "handy" to avoid threading state, they also lead to more brittle design and make it more difficult to understand which part of the code may or may not access them. As for single, there usually is no reason to enforce singularity in such a way; if your function takes a single argument of type `Display`, then it will only use one `Display`. Having the ability to create multiple `Display` can help parallelizing unit tests. – Matthieu M. Nov 30 '15 at 07:51
  • Doesn't answer the question in the slightest way. – Silidrone Jul 23 '22 at 14:56
  • @Silidrone: Frame challenges have ever been welcome on the site; as reflected by the score. It may not be the answer you want, but I assure you it's the answer you need. – Matthieu M. Jul 24 '22 at 11:13
15

Another option you overlook is namespace's.

namespace xyz {
namespace {
    int private_variable;
}

int get_pv() {
    return private_variable;
}
}

Functionally, this is going to be similar to your option #2, but you can't accidentally "delete" this thing. You can't accidentally create an instance of it. It is just a collection of related globally accessible data and functions. You can (as in my example) even have "private" members and functions.

Of course the usage would be something like this:

int x = xyz::get_pv();
Evan Teran
  • 87,561
  • 32
  • 179
  • 238
  • 1
    I was digging into the Internet for this. Wonder if there's any keywords to help me find more about it. – Seideun May 12 '22 at 06:48
1

You might add: static objects can throw exceptions. The executable will not to start and it's difficult to debug/handle well.

Jay
  • 13,803
  • 4
  • 42
  • 69
  • Its difficult to debug if naively implemented. If done correctly using a factory or service locater pattern then there are no problems in terms of debugging. – Martin York Oct 01 '10 at 16:43
  • 2
    Lots of effort where none is needed. Create a single global and you're done. – Jay Oct 04 '10 at 15:21
1

Let say we need to have just one instance of some class in our project. There are couple ways of doing it.

A better solution:

A variable in main that you pass as a parameter to all required functions would be another.

a) The order of initialization of static members across different units isn't defined. So, completely static members initialization can't use any static members/functions from other modules. And singleton doesn't have this problem.

Singletons do have this problem if their constructor/destructor access other global static lifespan variable.

b) We have to deal with threading for getInstance() of Sigleton. However, completely static class doesn't have this problem.

Not really a problem is it? If you are aware of it just add the appropriate locks in the code.

c) Access to methods looks a little bit different. Foo::bar(); vs Foo::getInstance()->bar(); Generally, sigleton can return NULL to identify that there were some problems with construction of object and static class can't.

I would make my getInstance() return a reference. Then there is no ambiguity over if the pointer is NULL. It either worked or threw an exception. Also this leads to a design where the destruction is correct called on the instance (Don't take this as advice to use Singleton I would avoid it if possible (but if you do use make it neat)).

d) Definition of class looks a little bit clunky with bunch of statics for static class.

No clunkier than writing a singleton properly.

The problem with both these methods is that they are both accessing global mutable state and thus the use of these 'single instance' objects by other objects is hidden from the user. This can lead to problems with testing (TDD requires the ability to mock external functionality but global mutable state prevents the ability of the tester from mocking external dependencies (easily)).

Any object that is non POD has a constructor that may potentially throw an exception. Thus for objects in the global namespace this means that exceptions can be thrown before main() is entered (this can lead to hard to find bugs (if you have a lot of global objects (you have to put breakpoints everywhere)). But the same problem exists with a singleton that is lazily evaluated; If on first use it throws how do you correct for this so that a subsequent attempt does not throw? Ow will your application continue throwing each time the singleton is retrieved?

AAEM
  • 1,837
  • 2
  • 18
  • 26
Martin York
  • 257,169
  • 86
  • 333
  • 562
1

You could also use the Borg pattern, which is a little more complicated in C++ unless you have access to a shared pointer class. The idea is that any number of Borg classes can be instantiated, but their state is shared across all instances.

Colin
  • 10,447
  • 11
  • 46
  • 54