简体   繁体   中英

Make a shared_ptr with only one owner

I have an object in C++11 that has only one owner. However, other objects can hold a weak_ptr to this object. This way, I can test if the object still exists before I use it.

For now, the owner has a shared_ptr to the object, that always has an owner count of 1.

How to ensure no one is allowed to make a copy of this shared_ptr ?

I can't use a unique_ptr because apparently C++11 does not allow to make a weak_ptr out of a unique_ptr .

EDIT : Thank you for your replies !

All the program runs on a single thread. The main owner has a periodical update loop, in which it calls all of its subsystems.

Thanks for pointing out that using a weak_ptr actually creates a shared_ptr . The expectation is that no subsystem or object is allowed to store a shared_ptr during a call to another non-const function. This way, any function can ask the owner to remove an object from its collection, and the object is deleted immediately.

I was hoping to avoid the overhead of increasing the ref count of the shared_ptr each time a subsystem calls lock() , since it is expected to release the ownership immediately (and ensure it is the case at compile time.)

EDIT 2 : Example code to formulate my problem more clearly

class Manager {
  public:
    Manager();           //Creates objects and stuff
    void updateAllObjects() {
      for (auto& o : mObjects)
        o->update(*this);
    }
    void deleteObject(weak_ptr<WorldObject>);    //May be called by other objects
    vector<shared_ptr<WorldObject>> mObjects;
};

class WorldObject {
  public:
    virtual void update(Manager&)=0;
};

class Passenger : public WorldObject {
  public:
    void update(Manager&);
};

class Car : public WorldObject {
  public:
    void update(Manager&);    //May call Manager::deleteObject on its passengers, or change them etc...
    //If Manager decides to delete passenger, they must be destroyed, so we don't want those references to count
    vector<weak_ptr<Passenger>> mPassengers;
    //A passenger can belong to several cars, and must be updated exactly once per WorldObject::update()
};

How to : - Avoid the overhead of storing many shared_ptr - Make sure Car does not store a shared_ptr to a passenger for longer than its Car::update() function

For now, the owner has a shared_ptr to the object, that always has an owner count of 1.

This is a false statement, as owner of std::weak_ptr only can use it by creating std::shared_ptr to it so owner count will be > 1. So your question is pretty much meaningless - if you want to provide only exclusive access to the object move std::unique_ptr around, if you want to use it through std::weak_ptr it must be shared and you cannot enforce single owner in principle.

You can't, the nature of the shared_ptr is that you can create more instances of it. What you can do, is to make it a private member of the class, and don't expose the shared_ptr to the external world directly, as you do now. You can't however be safe against a dumb used, someone could as well get a weak_ptr , extract the raw pointer and delete it. If the user of yoru class wants to shoot himself in the foot, there's nothing you can do!

You cannot do this without implementing your own valid_ptr or something similar since you can always get another shared_ptr via weak_ptr::lock . That is, only if you want to periodically check whether the object was deleted (ie not for checking if the object still exists or actually accessing it, see below!)

template<typename T>
class valid_ptr {
public:
     template<typename S>
     valid_ptr(const std::weak_ptr<S>& r) : m_ptr(r) { }
     bool expired() const { return m_ptr.expired(); }
private:
     std::weak_ptr<T> m_ptr;
};

If you actually want to access your pointer then you should simply use the weak_ptr how it is intended and aquire a shared_ptr for the duration of the access via weak_ptr::lock :

if (auto shared = weak.lock()) {
    // ptr is valid and can now be used via the shared_ptr shared
} else {
    // ptr is nullptr
}

You shouldn't simply rely on a positive expired check to access the object since between the check and the access anything can happen (including the object being deleted, unless you've got some additional constraints you didn't mention in your question). In that case you want to ensure (shared) ownership by acquiring a shared_ptr for the duration of the access.

Don't do it.

If you must:

Use a shared ptr to a mutex locking functor containing a wraping a writable unique ptr.

Everyone else gets a shared ptr to a mutex wrapping functor wrapping a possibly null immutable raw ptr, implemented by wrapping the above and exposing the .get() result in apply .

The owner functor is owned by one bit of code. It exposes the non-owner functors. It can destroy the unique ptr.

Non-owner functors cannot extend the lifetime of the object past their mutex lock.

Owner functors can set the unique ptr to nothing, and (once they have the mutex) nobody can stop them.

You could use shared mutexes to allow multiple readers.

What do I mean by functor?

A functor is a type that wraps another type that supports apply. If [T] is the functor type, and T the type wrapped, and T->U is the type of a function taking T and returning U , then the apply operation is of type:

([T],(T->U))->U

Or

[T].apply(T->U)

Returns a U .

If T is a type, you apply T->U to T to get U .

If [T] is a functor of T , then you apply it to T->U to get U . (Notice the slight change in wording compared to previous paragraph).

A functor of T knows how to run functions of T , but makes no claim to be a T .

Implementation wise, it either contains a T or knows how to get one when asked. It can make other guarantees about how it runs the functions (for example, guarded with a mutex, in a dedicated thread, on a server, whatever).

See Haskell for further information.

Also note that reentrancy cannot be supported with reasonable axioms, so deadlock on a single thread is possible.

This answer is not easy to understand: I am merely including it for completeness. Unless you came to C++ from a modern functional programming background, anyone asking the OP's question probably won't get this, and shouldn't try (I am merely trying to show the OP's request is possible , nkt that it is a good idea). What is worse is that I may have gotten some of the functional programming terminology wrong, if not the design.

How about using a std::unique_ptr to the object and instead of std::weak_ptr a raw pointer to this std::unique_ptr . This way you have only one owner to the object and you can test the validity of the std::unique_ptr through the raw pointer to it using the bool operator.

auto object = std::make_unique<T>(...); // creates object and std::unique_ptr
...
auto* object_user = &object; // raw pointer to the std::unique_ptr holding the object
...
if ( *object_user ) (*object_user)->... // using the object

Note: The ellipses are any code you need.

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