4

Recently I posted a question on SO regarding usage of a class which carried a bit of separate functionality that it should've, ideally. I was recommended to learn about singleton pattern so that only one instance is created of the class and it manages the set of operations revolving around the data it encapsulates. You can see the question here - using Static Container for base and derived classes .


Now consider this code -

#include <iostream>
#include <string>
#include <unordered_map>

class A{
    std::string id;
  public:
    A(std::string _i): id(_i){}
    virtual void doSomething(){std::cout << "DoSomethingBase\n";}
};

class B : public A{
    std::string name;
  public:
    B(std::string _n):name(_n), A(_n){}
    void doSomething(){std::cout << "DoSomethingDerived\n";}
};

namespace ListA{
    namespace{
        std::unordered_map<std::string, A*> list;   
    }
    void init(){
        list.clear();
    }
    void place(std::string _n, A* a){
        list[_n] = a;
    }
}


int main() {
    ListA::init();
    ListA::place("b1", new B("b1"));
    ListA::place("a1", new A("a1"));
    return 0;
}

Ignoring the fact that I'm still using raw pointers which are leaking memory if program doesn't terminates as it is, is this a good alternative to using global static variables, or a singleton?


With regard to previous question, I've reorganized class A(base class) and class B(derived classes) independent of a namespace that manages a list of these objects. So is this a good idea, or a totally bad practice? Are there any shortcomings for it?

A good singleton implementation I was suggested was as follows -

class EmployeeManager
{
    public:
        static EmployeeManager& getInstance()
        {
            static EmployeeManager    instance; // Guaranteed to be destroyed.
                                  // Instantiated on first use.
            return instance;
        }
    private:
        EmployeeManager() {};
        std::unordered_map<std::string, Employee&> list;
    public:
        EmployeeManager(EmployeeManager const&) = delete;
        void operator=(const&) = delete;
        void place(const std::string &id, Employee &emp){
            list[id] = emp;
        }
};

class Employee
{
    public:
        virtual void doSomething() = 0;
};

class Writer : public Employee
{
    private: 
        std::string name_;
    public:
        Writer(std::string name) : name_(name) {};
        void doSomething() { };
};

Honestly I've never tried singleton pattern and I'm shying away to use it directly since I've no prior experience and I would rather first use it in my pet projects.

Community
  • 1
  • 1
hg_git
  • 2,884
  • 6
  • 24
  • 43
  • 6
    Your reaction to someone recommending the singleton pattern should be like when you just met someone who's taking you to a smoky back room and after a few rounds of poker suggests that "we should make it interesting". – Kerrek SB Sep 05 '16 at 17:46
  • 2
    The singleton pattern is a way to *force* the user to only create one instance. Do you feel an urge to create multiple instances when you really need only one? If not, just create the object you need and be done with it! – Bo Persson Sep 05 '16 at 17:52
  • @BoPersson do I really need to create an object for my use case? Don't I just need a std::map with some associated specialized functions to it? – hg_git Sep 05 '16 at 17:54
  • 2
    @hg_git - I don't know exactly. My comment was more about the Singleton Pattern being world famous, and the Single Object Pattern ("I just need one object, so I created it") not having any articles or blog posts written about it. – Bo Persson Sep 05 '16 at 18:01
  • IMHO a singleton-ness should always be an implementation detail of a normal class. Because one day you may not want it to be a singleton any more. Singletons were invented for java, which is an abomination spawned in the broken minds of the damned. c# is its demonic twin. Reach for the light of a singletonless program. Singletons tightly couple components together and defeat unit testing. – Richard Hodges Sep 05 '16 at 19:02

3 Answers3

3

is this a good alternative to using global static variables, or a singleton?

no, because you might encounter another problem: static initialization order fiasco. There are ways to fix it - but with functions with static variables - which looks just like singletons.

... but why do you need a global variables (even in namespaces) or singletons? In you first example, it would be perfectly fine if instead of namespace ListA you had struct ListA - plus remove that namespace{. Then you have:

int main() {
    ListA list;
    list.init();
    list.place("b1", new B("b1"));
    list.place("a1", new A("a1"));
}

and it looks fine.

Then your singleton aproach, once again - no need for it - create variable of type EmployeeManager in your main function, if you need to use it in some other class, then pass it by reference or pointer.

marcinj
  • 48,511
  • 9
  • 79
  • 100
  • I don't know, everyone told me to have only one object at max. Probably because this might lead to bugs when declared two or more? tbh idk :/ – hg_git Sep 05 '16 at 18:07
  • still I need to ask, in my example if I have traditional files like a.h & a.cpp, similarlly for b.h and b.cpp and then another ListA.cpp with ListA namespace and use the above code, how would ` static initialization order fiasco` even happen? They're independent of each other and ListA.cpp is only providing some functions and a variable to operate upon :/ – hg_git Sep 05 '16 at 18:13
  • The problem will be when you will try to acces (for example inside a constructor-s) your global class instances before main gets executed. Your sample code does not suffer from it. – marcinj Sep 05 '16 at 18:23
1

I'm not sure if you know that already, but you need to remember that Singleton really is a global variable with lazy initialization.

Lazy initialization is a tool to fix a problem of having the object initialized always at the time when you really want to use it - be it for some real-program function, or initializing another, dependent object. This is done to delay initialization until the first moment when you use the object.

The static object is simply initialized at the moment when it first appears to need to be created - however when this moment really is, is undefined, at least in C++.

You can replace lazy initialization with the static initialization, but you must ensure somehow that the initialization happens in defined order.

Defining variables inside the namespace is nothing else than declaring the variables globally. Namespaces are open, rules inside the namespace are the same as outside the namespace, except the symbol resolution.

What you can do to enforce ordered initialization is to create one global variable with all dependent global objects inside, in the form of struct that will contain all them as fields (not static fields!). Note though that the exact order of initialization will be only ensured between objects being fields of that structure, not between them and any other global objects.

Ethouris
  • 1,791
  • 13
  • 18
0

Your question can be answered without any line of code, as it was answered by a lot of people in the past. Singletons are bad because your code will depend on one class and its implementation. What you want though is to have independent units which don't know about the implementations of the interfaces they talk to. Propagation of values / reference should (in fact it must be done for large maintainable systems) via reference passing from containing object to its child, an observer / event system or an event / message bus. Many frameworks use at leat two of these approaches ... I highly recommend sticking to best practices.

Florian Salihovic
  • 3,921
  • 2
  • 19
  • 26
  • 2
    Could you expand a little bit more on - "Propagation of values / reference should (in fact it must be done for large maintainable systems) via reference passing from containing object to its child, an observer / event system or an event / message bus." Short examples would do well for me. – hg_git Sep 05 '16 at 18:22