简体   繁体   中英

Alternatives to refcount for unique_ptr

In following code example, there should be exist one instance of struct A inside struct B as long as any object of B is living. Example works as expected.

#include <memory>
#include <iostream>
#include <mutex>

struct A
{
    A() { std::cout << "A() called" << std::endl; }
    ~A() { std::cout << "~A() called" << std::endl; }
};

struct B
{
    B()
    {
        std::cout << "B() called" << std::endl; 

        std::lock_guard<std::mutex> guard(mtx);
        if( !refCount )
        {
            a.reset( new A ); 
        }
        ++refCount;
    }

    ~B()
    {
        std::cout << "~B() called" << std::endl;
        std::lock_guard<std::mutex> guard(mtx);
        --refCount;
        if( !refCount )
        {
            a.reset( ); 
        }
    }

    static std::unique_ptr<A> a;
    static std::mutex mtx;
    static int refCount;
};

std::unique_ptr<A> B::a;
int B::refCount(0);
std::mutex B::mtx;

int main()
{
    {
        B b1; //B::a should be created here
        B b2;
    } //B::a should be destroyed here

    B b3; // B::a should be recreated here
} //B::a should be destroyed again here

See also http://coliru.stacked-crooked.com/a/fea428254933ee5c

My question: Is there an alternative (threadsafe!) implementation without a refcount? Is this maybe possible to solve with a construct of std::shared_ptr and std::weak_ptr ?

The only way to ensure that an object lives "as long as any object of B is living" is by keeping a refcount of B objects. It's the only realistic way to tell if there are any living B objects, if they are going to be arbitrarily created and destroyed as the program runs.

std::shared_ptr internally keeps refcounts, which are tracked atomically. It's probably a better idea to use those, rather than manually managing the refcount yourself; that way you don't have to meticulously implement all of the RAII constructors, or reinvent the wheel.

Here is the solution wanted:

#include <memory>
#include <iostream>
#include <mutex>
using std::shared_ptr;
using std::weak_ptr;

struct A
{
    A() { std::cout << "A() called" << std::endl; }
    ~A() { std::cout << "~A() called" << std::endl; }
};

struct B
{
    B()
    {   std::cout << "B() called" << std::endl;
        std::lock_guard<std::mutex> guard(mtx);
        if (!(ac =aw.lock()))
          aw =ac =shared_ptr<A>(new A);
    }

    ~B()
    {   std::cout << "~B() called" << std::endl;
    }
    shared_ptr<A> ac;
    static weak_ptr<A> aw;
    static std::mutex mtx;
};
weak_ptr<A> B::aw;
std::mutex B::mtx;

int main()
{
    {
        B b1; //B::a should be created here
        B b2;
    } //B::a should be destroyed here

    B b3; // B::a should be recreated here
} //B::a should be destroyed again here

which produces the same output as your example:

B() called
A() called
B() called
~B() called
~B() called
~A() called
B() called
A() called
~B() called
~A() called

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM