C++ singleton vs completely static object

2019-01-21 19:16发布

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?

5条回答
我命由我不由天
2楼-- · 2019-01-21 19:36

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.

查看更多
可以哭但决不认输i
3楼-- · 2019-01-21 19:38

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();
查看更多
时光不老,我们不散
4楼-- · 2019-01-21 19:46

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?

查看更多
Lonely孤独者°
5楼-- · 2019-01-21 19:54

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

查看更多
地球回转人心会变
6楼-- · 2019-01-21 20:01

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.

查看更多
登录 后发表回答