0

Providing the simple implementation of a Singleton class below. It is possible for anybody to call the destructor as long as s/he has the reference to the singleInstance.

class SomeClass {
public: /** Singleton **/
    static SomeClass &instance() {
        static SomeClass singleInstance;

        return singleInstance;
    };

private: 
    SomeClass() = default;
    SomeClass(const SomeClass&) = delete;
    SomeClass &operator=(const SomeClass&) = delete;
};

To prevent such a nonsense operation, should we declare the destructor of Singleton classes in a private context?

class SomeClass {
    // ... same above
private: 
    ~SomeClass() {}
};

The problem exists also for the heap-allocated Singleton instances. Consider the implementation below.

class SomeClass {
public: /** Singleton **/
    static SomeClass &instance() {
        static SomeClass *singleInstance = nullptr;

        if(!singleInstance) {
            singleInstance = new SomeClass;
        }

        return *singleInstance;
    };

private: 
    SomeClass() = default;
    SomeClass(const SomeClass&) = delete;
    SomeClass &operator=(const SomeClass&) = delete;

    // ~SomeClass() {}
};

int main() 
{
    SomeClass *const ptr = &SomeClass::instance();

    delete ptr; // Compiles if destructor isn't private and vice versa

    return 0;
}
Caglayan DOKME
  • 948
  • 8
  • 21
  • Ask one question per post. – Passer By Mar 20 '22 at 10:10
  • 1
    If you really want to mess things up, you're able to call the destructor expicitly, but usually no programmer will do so, unless they want to mess with the program on purpose: `SomeClass::instance().~SomeClass()`; making the destructor private prevents this, but since noone should be doing this anyways, it's imho not worth the time... – fabian Mar 20 '22 at 10:15
  • @fabian What about the operator `delete`? The problem isn't just about the explicit call to the destructor. – Caglayan DOKME Mar 20 '22 at 10:21
  • 2
    How is deleting a singleton different from deleting anything else you don't own? I mean that problem exists for any class. (Like `SomeClass a; delete &a;`, `std::shared_ptr a = std::make_shared(); delete a.get();`, …) – t.niese Mar 20 '22 at 10:31
  • 2
    You can do all sorts of crazy things(`delete &SomeClass::instance();`), but basicaly every programmer knows they'll end up with undefined behaviour; but at the same time you could just reinterpret_cast the pointer to e.g. a `std::vector*` and delete that one, but at some point you need to trust the programmer to not do bs like that... If this code used only internally in a lib or only by yourself, you can be pretty certain you won't do this to your own program; in that case there's absolutely no benefit in explicitly hiding members of the class you now will only be used correctly... – fabian Mar 20 '22 at 10:35
  • 2
    The important thing is not to prevent it but to make the ownership clear in your code. If the ownership isn't clear for the one using the singleton, then how should it be clear for any other object in your code? (For a singleton it should be pretty clear compared to any other object). So if you fear that someone deletes the singleton, then any other object created from a "regular" class is even more problematic. – t.niese Mar 20 '22 at 10:46
  • 1
    Or put in another way, you can't prevent people from writing wrong code. What you _can_ do is make it abundantly clear what is correct code. – Passer By Mar 20 '22 at 12:01
  • You probably just want to do this: `static SomeClass &instance() { static SomeClass inst; return inst; }`. That’s threadsafe and cleans up after itself. – Ben Mar 22 '22 at 04:39

1 Answers1

1

Should we declare the desctructor of a Singleton class as private?

Yes.

Sidenote: Singleton pattern is rarely necessary, an using it unnecessarily is a common anti pattern.

eerorika
  • 232,697
  • 12
  • 197
  • 326
  • So, [who is responsible for destructing the block-scoped static Singleton instance?](https://stackoverflow.com/questions/71545818) – Caglayan DOKME Mar 20 '22 at 11:22
  • 1
    @CaglayanDOKME Objects with static storage duration are destroyed automatically after `main` returns. The language implementation takes care of it. – eerorika Mar 20 '22 at 11:24
  • 1
    How is it possible to call a `private` destructor? This is what I cannot understand. – Caglayan DOKME Mar 20 '22 at 11:39
  • @CaglayanDOKME You can call the destructor from the scope of the class. Just like any other private member function. – eerorika Mar 20 '22 at 11:42
  • For a self-aware singleton class (*cough* anti-pattern *cough*), which is intended to leak at termination (in a desperate attempt to address order-of-destruction issues at program termination), the destructor can be declared `= delete;`. `static SomeClass& instance() { static SomeClass* leak = new SomeClass; return *leak; }` – Eljay Mar 20 '22 at 15:46
  • @eerorika The problem is that the destructor isn't called from the class scope. – Caglayan DOKME Mar 20 '22 at 19:38