To singleton or not to singleton, that is the question....
-
@JonB said in To singleton or not to singleton, that is the question....:
Sigh, that's what I mean, I still find plenty out there advocating using singletons and now you say avoid, that is what I feared when I thought I had got to grips with it... ;-)
The original design patterns book just had a look at a lot of existing source code and unified the patterns that occured over and over again. And they gave names to these patterns. This means that a singleton is an obvious solution to a problem such that a lot of people came up with the idea. It does not mean it is the best solution. My understanding is that singletons should be avoided because they make your code untestable (here is the long version with explanations how to replace singletons: https://youtu.be/f46jmm7r8Yg?si=0lkErLBA2Xqj5rU6).
@JonB said in To singleton or not to singleton, that is the question....:
We are also told to avoid global functions....!
Well, this advice is most likely related to the purest form of object oriented programming. In OOP there are only objects and no free standing functions. However, C++ is a multiparadigm language. Bjarne Stroustrup himself advocates to use the best tool for the solution. OOP is not always the best tool. "When all you have is a hammer, everything looks like a nail." But, C++ has a hammer, a wrench, etc. Use these tools where appropriate. BTW, some people in the C++ community are moving away from OOP. "Data-oriented design" and "value-oriented programming" are the new buzzwords. E.g. algorithms are not objects. Does it make any sense to write
Math.sqrt(x);
in Java? Or is it better if you can writesqrt(x);
? I prefer the latter.@JonB said in To singleton or not to singleton, that is the question....:
Also with a class I can declare variables or methods public or private. namespace does not have that (all is public, you have to wrap inside a second, nested, anonymous namespace to achieve private).
With namespaces everything inside the header is public and everything that can only be found in the .cpp file is private. Using an unnamed namespace or marking free functions static just hides them during link-time as well. And you should really avoid global variables (i.e. also any kind of variables inside of namespaces). There is the all too common problem of the initialization order. Instead the workaround would be to use a function
int &getMyVar() { static int myVar; return myVar; }
. Once they have matured, modules will be the correct answer instead of namespaces. You have to explicitly export any functions or variables (i.e. make them 'public').@JonB said in To singleton or not to singleton, that is the question....:
So would it be that this example works fine without class/singleton but another case might not?
Yes, in this case a singleton is the suboptimal solution. There are other cases where they make more sense.
-
@SimonSchroeder
Yep, all good, a few comments from me.For the global stuff, @jsulm made a good point when he said "What you should avoid are global class instances/objects". I agree global functions are not so harmful.
Does it make any sense to write Math.sqrt(x); in Java? Or is it better if you can write sqrt(x);? I prefer the latter.
I think many years of C# programming in the past makes me think of everything having to be the former not the latter. There are no "free functions", everything must be in some class. Maybe same as Java, I don't know. And of course it's actually
std::sqrt
unless you areusing std
.With namespaces everything inside the header is public and everything that can only be found in the .cpp file is private. Using an unnamed namespace or marking free functions static just hides them during link-time as well
Ah, this is good! I was thinking I had to declare all my functions inside the
namespace
in the header header as I would have to do forclass
but everything beingpublic
without anyprivate
keyword. To test I tried writingvoid RandomNumber::foo() { }
in the.cpp
(nothing in the.h
) but that gives me... should have been declared inside ‘RandomNumber’
. To achieve a.cpp
-only function I found I had to use the "alternative" syntax of:namespace RandomNumber { void foo() {} }
or I could "add to" the namespace first in the
.cpp
and use:// top of file namespace RandomNumber { void RandomNumber::foo(); } ... // later on void RandomNumber::foo() {}
As an aside, this is just what I wish most that C++ had for
class
es. One of the things I hate most is that you have to put all your method declarations in the header file. I loathe having to put theprivate
ones there, and consequently as well having to put in whatever#include
s into the.h
just to support anything inprivate
. Ifclass
es could have theirprivate
s in the.cpp
only as per the second way above it would be so much better, but I know you can't have "partial" classes in C++ which you can augment e.g. in the.cpp
.workaround would be to use a function
int &getMyVar() { static int myVar; return myVar; }
Yes, that is what my original singleton pattern code used for its
static RandomNumber& instance()
method.@SimonSchroeder said in To singleton or not to singleton, that is the question....:
Yes, in this case a singleton is the suboptimal solution. There are other cases where they make more sense.
Agreed. I have changed all my stuff for this case to namespace, I have learned a lot about them so I am confident now going forward. I will worry about "other cases", perhaps when what I need really is a class for some reason, as and when I encounter them.
-
@JonB said in To singleton or not to singleton, that is the question....:
should have been declared inside ‘RandomNumber’
This should work (in .cpp):
void someHelperFunction() { } namespace RandomNumber { void foo() { someHelperFunction(); } }
-
@JonB said in To singleton or not to singleton, that is the question....:
consequently as well having to put in whatever #includes into the .h just to support anything in private
In case of pointers you can avoid the includes by using forward declarations.
Often d pointer pattern is used to hide implementation details and keep binary compatibility while changing internal implementation.
-
@jsulm
Hi, not sure if you are adding something here? I wrote above that, with nothing in the.h
file, you can indeed put in the .cpp
namespace RandomNumber { void foo() { ... } }
What I said I found you cannot do is
void RandomNumber::foo() { ... }
For that, if you are not going to put
namespace RandomNumber { void foo(); }
in the
.h
(because you want to only use it privately) then I found you can/must add it in the.cpp
--- this adding to thenamespace
declarations from the.h
--- before defining it viavoid RandomNumber::foo() { ... }
.[I never had a question about some global/free
someHelperFunction()
function.] -
@jsulm said in To singleton or not to singleton, that is the question....:
In case of pointers you can avoid the includes by using forward declarations.
Of course, and I do sometimes do that to avoid being forced to pull in a full
#include
. But when, say, you have a whole load ofprivate
methods in a class and they use types (copy or reference) as parameters/returns which are either from your own "internal" types or from external types you get forced to have the full#include
s in the.h
even if only relevant to yourprivate
methods. I hate that. -
@JonB said in To singleton or not to singleton, that is the question....:
then I found you can/must add it in the .cpp --- this adding to the namespace declarations from the .h
One of the nice things about namespaces is that you can reopen them as often as you want to. There are even a few cases where you are supposed to add something to namespace
std
.@JonB said in To singleton or not to singleton, that is the question....:
they use types (copy or reference) as parameters/returns
Just like pointers, references are also find and just need forward declarations. If your class has more than one or two member variables (of built-in type) it is most likely cheaper to use a reference instead of a copy. I only have very few cases where I'd want a copy of an object. That being said, I just noticed that I don't know if r-value references
&&
also work with forward declarations. Does anybody know? -
@SimonSchroeder r-value references && work with forward declarations.
-
@jsulm if void baz() is defined in two namespaces, Calling baz() with using "using Foo;" can cause confusing while two namespaces are present at the same place. Therefore, Foo:: prefix is preferred.
namespace Foo1 { void baz() { ... } } namespace Foo2 { void baz() { ... } } In another class: using Foo1; using Foo2; void AnotheClass::testing() { baz(); // not clear } =================== void AnotheClass::testing() { Foo1::baz(); // clear }
-
I agree that in general you should avoid
using
(for namespaces; now also allowed as a replacement fortypedef
).There are, however, a few select places where I do use it for namespaces: 1) When using
swap
the preferred method is tousing std::swap;
(technically not a namespace) and then callswap
unqualified (without a namespace, class name, etc). 2) For using string""s
and string view""sv
literals. Haven't usedstd::chrono
much, but most likely would import the suffixes as well. -
@JoeCFD said in To singleton or not to singleton, that is the question....:
Calling baz() with using "using Foo;" can cause confusing while two namespaces are present at the same place.
In the concrete example you have given, it would not compile because the compiler cannot resolve the overload (i.e. Foo1::baz or Foo2::baz). You'd still need to use a fully qualified name. It only gets confusing when you have
Foo1::baz(int)
andFoo2::baz(double)
. You might writebaz(1);
when you actually want to call Foo2::baz. The software might even do the right thing for years. Someone might just introduce Foo1::baz years down the line and suddenly the behavior changes.