Singleton instance as static field versus static variable in getInstance () method

In this thread , individual instances are referred to:

A static variable can be static for the GetInstance () function, or it can be static in the Singleton class. There are interesting compromises.

What are these tradeoffs? I know that if declared as a function variable static, a singleton will not be created until the function is called. I also read something about thread safety, but I don’t know what exactly this entails, or how the two approaches differ in this regard.

Are there other significant differences between the two? Which approach is better?

In my specific example, I have a factory class configured as single, and I store the instance as a field static constin the class. I have no technique getInstance(), but rather expect that the user will directly access the instance: ItemFactory::factory. The default constructor is private, and the instance is allocated statically.

Addendum: how good is the idea of ​​overloading operator()to call a method createItem()for singleton, so it Itemcan be created as follows ItemFactory::factory("id"):?

+4
source share
3 answers

What are these tradeoffs?

It's the most important:

static . - static , static .

local static . , , , . . , , undefined.

, , , main. , , . static , . , ++ 11, , , .

static.

?

, .

+2

. ++ . GNU ++ . Visual Studio 2015 . , , .

, , , , , . , , .

class ItemFactory {
static std::atomic_flag initialized = ATOMIC_FLAG_INIT;
static std::unique_ptr<ItemFactory> theFactoryInstance;
public:
static ItemFactory& getInstance() {
  if (!initialized.test_and_set(std::memory_order_acquire)) {
    theFactoryInstance = std::make_unique<ItemFactory>();
  }
  return *theFactoryInstance;
}
};

, main(). , .

, . private , .

, () factory . operator() "", factory "create".

+2

singleton ++?

, .

?

. , , (, , , ).

?

, , . , . , , / , .

, , ?

.

, .

:

auto j1 = jobbie();
auto j2 = jobbie();
auto j3 = jobbie();

j1.log("doh");
j2.log("ray");
j3.log("me");

{
    shared_file f;
    f.log("hello");
}

{
    shared_file().log("goodbye");
}

shared_file().log("here another");

shared_file f2;
{
    shared_file().log("no need to reopen");
    shared_file().log("or here");
    shared_file().log("or even here");
}
f2.log("all done");

where the object jobbieis just the facade for a single element, but the object shared_filewants to reset / close itself when not in use.

therefore, the output should look like this:

doh
ray
me
opening file
logging to file: hello
closing file
opening file
logging to file: goodbye
closing file
opening file
logging to file: here another
closing file
opening file
logging to file: no need to reopen
logging to file: or here
logging to file: or even here
logging to file: all done
closing file

We can achieve this using an idiom that I will call "value-semantics-is-a-facade-for-singleton":

#include <iostream>
#include <vector>

// interface
struct jobbie
{
    void log(const std::string& s);

private:
    // if we decide to make jobbie less singleton-like in future
    // then as far as the interface is concerned the only change is here
    // and since these items are private, it won't matter to consumers of the class
    struct impl;
    static impl& get();
};

// implementation

struct jobbie::impl
{
    void log(const std::string& s) {
        std::cout << s << std::endl;
    }
};

auto jobbie::get() -> impl& {
    //
    // NOTE
    // now you can change the singleton storage strategy simply by changing this code
    // alternative 1:
    static impl _;
    return _;

    // for example, we could use a weak_ptr which we lock and store the shared_ptr in the outer
    // jobbie class. This would give us a shared singleton which releases resources when not in use

}

// implement non-singleton interface

void jobbie::log(const std::string& s)
{
    get().log(s);
}

struct shared_file
{
    shared_file();

    void log(const std::string& s);

private:
    struct impl;
    static std::shared_ptr<impl> get();
    std::shared_ptr<impl> _impl;
};

// private implementation

struct shared_file::impl {

    // in a multithreaded program
    // we require a condition variable to ensure that the shared resource is closed
    // when we try to re-open it (race condition)
    struct statics {
        std::mutex m;
        std::condition_variable cv;
        bool still_open = false;
        std::weak_ptr<impl> cache;
    };

    static statics& get_statics() {
        static statics _;
        return _;
    }

    impl() {
        std::cout << "opening file\n";
    }
    ~impl() {
        std::cout << "closing file\n";
        // close file here
        // and now that it closed, we can signal the singleton state that it can be
        // reopened

        auto& stats = get_statics();

        // we *must* use a lock otherwise the compiler may re-order memory access
        // across the memory fence
        auto lock = std::unique_lock<std::mutex>(stats.m);
        stats.still_open = false;
        lock.unlock();
        stats.cv.notify_one();
    }
    void log(const std::string& s) {
        std::cout << "logging to file: " << s << std::endl;
    }
};

auto shared_file::get() -> std::shared_ptr<impl>
{
    auto& statics = impl::get_statics();

    auto lock = std::unique_lock<std::mutex>(statics.m);
    std::shared_ptr<impl> candidate;
    statics.cv.wait(lock, [&statics, &candidate] {
        return bool(candidate = statics.cache.lock())
        or not statics.still_open;
    });
    if (candidate)
        return candidate;

    statics.cache = candidate = std::make_shared<impl>();
    statics.still_open = true;
    return candidate;
}


// interface implementation

shared_file::shared_file() : _impl(get()) {}
void shared_file::log(const std::string& s) { _impl->log(s); }

// test our class
auto main() -> int
{
    using namespace std;

    auto j1 = jobbie();
    auto j2 = jobbie();
    auto j3 = jobbie();

    j1.log("doh");
    j2.log("ray");
    j3.log("me");

    {
        shared_file f;
        f.log("hello");
    }

    {
        shared_file().log("goodbye");
    }

    shared_file().log("here another");

    shared_file f2;
    {
        shared_file().log("no need to reopen");
        shared_file().log("or here");
        shared_file().log("or even here");
    }
    f2.log("all done");


    return 0;
}
+1
source

Source: https://habr.com/ru/post/1621106/


All Articles