1

There's two containers: owner and non-owner of resource. As I have only 1 owner, I suppose I need unique_ptr.

class OwnershipContainer {
public:
    void add(std::unique_ptr<Obj> obj) {
        objects.push_back(std::move(obj));
    }
    Obj* get(std::size_t i) { return objects[i].get(); }
private:
    std::vector<std::unique_ptr<Obj>> objects;
};

What of kind of pointer i have to use for non-owner container? First thought was raw pointer. But i cannot give guarantee that lifetime of Obj match or exceed lifetime of Non-owner container.

class NonOwnershipContainer {
public:
    void add(Obj *obj) {
        objects.push_back(obj);
    }
    Obj* get(std::size_t i) { return objects[i]; }
private:
    std::vector<Obj*> objects;
};

int main() {
    NonOwnershipContainer nonOwnershipContainer;
    {
        OwnershipContainer ownershipContainer;
        ownershipContainer.add(std::make_unique<Obj>(1));

        nonOwnershipContainer.add(ownershipContainer.get(0));
    }
    auto pobj = nonOwnershipContainer.get(0); // dangling pointer
}

I could use shared_ptr for owner and weak_ptr for non-owner, so I could check if weak_ptr is expired or not. But shared_ptr means that i have shared ownership, that's not true in my case, and I no need reference counter.

EDIT:

I'm not trying to extend lifetime. When owner container is destroyed, I want to avoid dangling pointers. As I wrote above, I could use shared_ptr + weak_ptr.

class OwnershipContainer {
public:
    void add(std::shared_ptr<Obj> obj) {
        objects.push_back(obj);
    }
    std::shared_ptr<Obj> get(std::size_t i) { return objects[i]; }
private:
    std::vector<std::shared_ptr<Obj>> objects;
};


class NonOwnershipContainer {
public:
    void add(std::shared_ptr<Obj> obj) {
        objects.push_back(obj);
    }
    std::shared_ptr<Obj> get(std::size_t i) { return objects[i].lock(); }
private:
    std::vector<std::weak_ptr<Obj>> objects;
};

int main() {
    NonOwnershipContainer nonOwnershipContainer;
    {
        OwnershipContainer ownershipContainer;
        ownershipContainer.add(std::make_shared<Obj>(1));

        nonOwnershipContainer.add(ownershipContainer.get(0));
    }
    auto pobj = nonOwnershipContainer.get(0); // no more dangling pointer, pobj == nullptr
}

But in this case, I pay for reference counter, and it is philosophically wrong: with only one owner use shared_ptr.

Oleksii.al
  • 55
  • 6
  • What's the argument against a plain `std::vector`? What you're doing in main looks *very* dangerous. Essentially you're trying to extend lifetime, which means you got the ownership wrong. – stefan Apr 23 '17 at 09:49
  • I use inheritance and polymorphism, Obj is a base class. – Oleksii.al Apr 23 '17 at 09:51
  • 1
    If the owning container is supposed to be the sole owner, then all access to the objects has to go through it. – StoryTeller - Unslander Monica Apr 23 '17 at 09:53
  • 1
    "and I no need reference counter." -- Isn't a way to check if the reference count has reached zero *exactly* what you're asking for? –  Apr 23 '17 at 09:56
  • Do you want to avoid dangling pointers by disallowing them (that is, not using the NonOwnershipContainer...) or by making them point to something meaningful (that is, using shared pointers)? – stefan Apr 23 '17 at 10:03
  • @hvd if I'll use shared_ptr with reference counter, then when owner will be destroyed, the object will still alive because reference counter will equal 1. I'd like to destroy obj at the moment when owner container was destroyed and to reset pointer in non-owner container, that i'll can remove it later. – Oleksii.al Apr 23 '17 at 10:19
  • 1
    `weak_ptr` is not for checking, it's for *locking* the object. It makes no sense to check if an object is alive if it can die while you are playing with it. You assign a `weak_ptr` to a `shred_ptr` (that's the locking action) and then check the `shared_ptr`. If it's alive, it will stay alive. It looks like exactly what you need. – n. 'pronouns' m. Apr 23 '17 at 10:30
  • @Arkaniy You've already shown in your own edit that that isn't true, haven't you? Your object does get destroyed when the owner collection is, that's why you're getting `nullptr` from your `get`. –  Apr 23 '17 at 10:32
  • @hvd yes, my object does get destroyed and pointer in non-owner container is nullptr. It's exatly that what i want to have. But in fact, reference counter is always 1(when object is alive), that's why i wrote, that i no need it. And in this case I use shared_ptr for sole owner. My question is, is there another(right) way to get the behavior, that i have in my own edit? – Oleksii.al Apr 23 '17 at 10:46
  • 1
    @Arkaniy "But in fact, reference counter is always 1(when object is alive)," -- In other words, it's not always 1. It can go to 0. But, no, when it's non-zero, it's not always 1 either. Your `get` returns a `shared_ptr`, so it increases the reference count. This is good: without that, it'd be possible to check that the object is alive, but having it destroyed from the owner collection while doing things with it. –  Apr 23 '17 at 10:50

1 Answers1

3

You actually do have shared ownership: When you access the object via the NonOwningContainer-contained pointer-like thing, you must take ownership, or the object may disappear out from under you while you're working with it.

Since you can't guarantee the object won't disappear out from under you:

But i cannot give guarantee that lifetime of Obj match or exceed lifetime of Non-owner container.

then your only option is to share ownership. Hence shared_ptr and weak_ptr is the appropriate approach.

Also, depending on the difference in lifetime of OwnershipContainer and NonOwnershipContainer, be aware of the interaction between std::make_shared and std::weak_ptr.

Community
  • 1
  • 1
TBBle
  • 1,287
  • 9
  • 21