-3
#include <iostream>

using namespace std;

class MonsterDB
{
private:
    ~MonsterDB() {}; // private destructor prevents instances on stack

public:
    static void DestroyInstance(MonsterDB* pInstance)
    {
        delete pInstance; // member can invoke private destructor
    }

     void DoSomething() {} // sample empty member method
};

int main()
{
    MonsterDB* myDB = new MonsterDB(); // on heap
    myDB->DoSomething();
    
    // uncomment next line to see compile failure
    // delete myDB; // private destructor cannot be invoked
    
    // use static member to release memory
    MonsterDB::DestroyInstance(myDB);

    return 0;
}

Here MonsterDB is a class that prohibits Instantiation the stack. So the way to implement is to hide the destructor. So he created a method to clear the variable on heap. I understood till here. But why did he choose to make the method static? Even if it not, we can still access the method. Am I correct? Also can someone point me to what is the use of static method? (I understood the use of static member, that is uniformity across all the instances, also that only static method can access static members).

paulsm4
  • 114,292
  • 17
  • 138
  • 190
V B
  • 75
  • 5
  • 3
    No don't do this. You will loose all the power of destructors. If you want this then add a public create method that returns a std::unique_ptr using [std::make_unique](https://en.cppreference.com/w/cpp/memory/unique_ptr/make_unique) to a MonsterDB (try to avoid using new/delete in your code) and make your constructor protected (or private if MonsterDB is [final](https://en.cppreference.com/w/cpp/language/final)) – Pepijn Kramer Jun 11 '23 at 05:12
  • Relevant: https://stackoverflow.com/questions/1008019/c-singleton-design-pattern?r=Saves_AllUserSaves – πάντα ῥεῖ Jun 11 '23 at 05:42
  • 1
    You are basically correct, the static method could be replaced with a non-static method that does `delete this;`. But as everyone else is saying this is poor code. Why exactly is it important to prevent creation on the stack? – john Jun 11 '23 at 05:56
  • But as John is saying, why not have a normally creatable MonsterDB class (with copy/move constructors of your choice). As long as MonsterDB internally uses stl containers or dynamic allocation for its (big) data all the extra stuff is not needed. When designing software avoid "accidental complexity". – Pepijn Kramer Jun 11 '23 at 07:43
  • @PepijnKramer: I started learning C++ and didn't reach till STL (going through OOP concepts). But I will look at this. – V B Jun 11 '23 at 07:56
  • @john: This is what the author says "Space on the stack is often limited. If you are writing a database that may contain terabytes of data in its internal structures, you might want to ensure that a client of this class cannot instantiate it on the stack; instead it is forced to create instances only on the free store. The key to ensuring this is declaring the destructor private". – V B Jun 11 '23 at 07:56
  • Just know OOP is not only inheritance. Inheritance is just a part. And I advise you to first learn more about using the STL containers (and std::make_unique) at least before you start inventing your own datastructures. Otherwise you will teach yourself to use new/delete too much. – Pepijn Kramer Jun 11 '23 at 08:02
  • 2
    If you're interested : Good sources to learn cpp from are : A [recent C++ book](https://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list) or have a go at https://www.learncpp.com/ (that's pretty decent, and pretty up-to-date). For C++ reference material use : [cppreference](https://en.cppreference.com/w/). And after you learned the C++ basics from those sources, look at the [C++ coreguidelines](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines) regularely to keep up-to-date with the latest guidelines. – Pepijn Kramer Jun 11 '23 at 08:03
  • 2
    @VB Take for example a `std::vector` it could contain terabytes of data in it's internal structures but that data will be allocated on the heap, because that's how `std::vector` works. So there is no problem declaring a `std::vector` on the stack, even if it contains terabytes of data. Any sensibly defined class would work in the same way. The author is confusing implementation with use. And the second statement 'The key to ensuring this ...' is patently incorrect as the answer below demonstrates. – john Jun 11 '23 at 08:09

1 Answers1

3

When using a static create method you don't have to change the destructor and introduce a new manual mechanism for destroying objects. Calls to DestroyObject would just be too easy to forget to add to your code. Create methods are not, because you will not get any instance if you don't call them.

So here is the code with a create method which also returns a unique_ptr to help with automatic memory cleanup at exit.

#include <iostream>
#include <memory>

// using namespace std; no don't do this

class MonsterDB final
{
public:
    // A static function can be called
    // without having an instance of MonsterDB
    static std::unique_ptr<MonsterDB> Create()
    {
        // normally make_unique works like this
        // but it doesn't work with a private constructor
        // return std::make_unique<MonsterDB>(); 

        // so use the syntax with a rare explicit new 
        return std::unique_ptr<MonsterDB>(new MonsterDB());
    }

    void DoSomething()
    {
        std::cout << "sample empty member method\n";
    }

    ~MonsterDB() = default;

private:
    MonsterDB() = default;

    // make non copyable to avoid copies on stack
    MonsterDB(const MonsterDB&) = delete;
    MonsterDB& operator=(const MonsterDB&) = delete;

    // make non movable to avoid moves to stack
    MonsterDB(MonsterDB&&) = delete;
    MonsterDB& operator=(MonsterDB&&) = delete;
};


int main()
{
    // monster_db will be a std::unique_ptr now
    // this is great because its destructor will
    // free the memory when it goes out of scope 
    // at the end of main
    auto monster_db = MonsterDB::Create();
    monster_db->DoSomething();

    return 0;
}
Pepijn Kramer
  • 9,356
  • 2
  • 8
  • 19