Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
338 views
in Technique[技术] by (71.8m points)

c++ - shared_ptr<> is to weak_ptr<> as unique_ptr<> is to... what?

In C++11, you can use a shared_ptr<> to establish an ownership relation with an object or variable and weak_ptr<> to safely reference that object in a non-owned way.

You can also use unique_ptr<> to establish an ownership relation with an object or variable. But what if other, non-owning objects want to also reference that object? weak_ptr<> isn't helpful in this case. Raw pointers are helpful but bring various downsides (e.g. they can be automatically initialized to nullptr but this is accomplished through techniques that are not consistent with the std::*_ptr<> types).

What is the equivalent of weak_ptr<> for non-owning references to objects owned via unique_ptr<>?

Here's a clarifying example that resembles something in a game I'm working on.

class World
{
public:

    Trebuchet* trebuchet() const { return m_trebuchet.get(); }

private:
    std::unique_ptr< Trebuchet > m_trebuchet;
};

class Victim
{
public:
    Victim( Trebuchet* theTrebuchet ) : m_trebuchet( theTrebuchet ) {}

    ~Victim()
    {
        delete m_trebuchet;     // Duh. Oops. Dumb error. Nice if the compiler helped prevent this.
    }

private:

    Trebuchet* m_trebuchet;    // Non-owning.
};

shared_ptr< Victim > createVictim( World& world )
{
    return make_shared< Victim >( world.trebuchet() );
}

Here we use a raw pointer to maintain a non-owning relationship with an object owned via unique_ptr<> elsewhere. But is raw the best we can do?

The hope is a type of pointer that:

  • Looks like the other modern pointer types. E.g. std::raw_ptr<T>.
  • Replaces raw pointers so that a codebase that uses modern pointer types throughout can find all pointers via a search for _ptr< (roughly).
  • Auto-initializes to nullptr.

Thus:

int* p;                  // Unknown value.
std::raw_ptr< int > p;   // null.

Does this type already exist in C++ now, is it proposed for the future, or is another implementation broadly available in e.g. Boost?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The "notify" behavior of shared_ptr requires reference counting the reference count control block. shared_ptr's reference count control block(s) use separate reference counts for this. weak_ptr instances maintain references to this block, and weak_ptrs themselves prevent the reference count control block from being deleteed. The pointed-to object has its destructor called when the strong count goes to zero (which may or may not result in deleteion of the memory where that object was stored), and the control block is deleteed only when the weak reference count goes to zero.

unique_ptr's tenet is that it has zero overhead over a plain pointer. Allocating and maintaining reference count control blocks (to support weak_ptr-ish semantics) breaks that tenet. If you need behavior of that description, then you really want shared semantics, even if other references to the object are non-owning. There's still sharing going on in that case -- the sharing of the state of whether or not the object has been destroyed.

If you need a generic nonowning reference and don't need notification, use plain pointers or plain references to the item in the unique_ptr.


EDIT:

In the case of your example, it looks like Victim should ask for a Trebuchet& rather than a Trebuchet*. Then it's clear who owns the object in question.

class World
{
public:

    Trebuchet& trebuchet() const { return *m_trebuchet.get(); }

private:
    std::unique_ptr< Trebuchet > m_trebuchet;
};

class Victim
{
public:
    Victim( Trebuchet& theTrebuchet ) : m_trebuchet( theTrebuchet ) {}

    ~Victim()
    {
        delete m_trebuchet;     // Compiler error. :)
    }

private:

    Trebuchet& m_trebuchet;    // Non-owning.
};

shared_ptr< Victim > createVictim( World& world )
{
    return make_shared< Victim >( world.trebuchet() );
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...