15

I want to have a class with a pointer member variable. This pointer should point to an object which may be stack-allocated or heap-allocated. However, this pointer should not have any ownership. In other words, no delete should be called at all when the pointer goes out of scope. I think that a raw pointer could solve the problem... However, I am not sure if there is a better C++11 approach than raw pointers?

Example:

class foo{
public:
    bar* pntr
};

int main(){
    bar a;
    foo b;
    b.pntr=&a;
}
Patryk
  • 22,602
  • 44
  • 128
  • 244
Humam Helfawi
  • 19,566
  • 15
  • 85
  • 160

5 Answers5

23

Raw pointers are perfectly fine here. C++11 doesn't have any other "dumb" smart pointer that deals with non-owning objects, so you cannot use C++11 smart pointers. There is a proposal for a "stupid" smart pointer for non-owned objects:

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4282.pdf

already implemented experimentally as std::experimental::observer_ptr (thanks @T.C. for the hint).

Another alternative is to use a smart pointer with a custom deleter that doesn't do anything:

#include <memory>

int main()
{
    int a{42};

    auto no_op = [](int*){};
    std::unique_ptr<int, decltype(no_op)> up(&a, no_op);
}

or, as mentioned by @T.C. in the comment, a std::reference_wrapper.

As mentioned by @Lightness Races in Orbit, a std::weak_ptr may also be a solution, as the latter is also a non-owning smart pointer. However a std::weak_ptr can only be constructed from a std::shared_ptr or another std::weak_ptr. A serious downside is that the std::shared_ptr is a "heavy" object (because of the internal reference counting mechanism). Note that even in this case the std::shared_ptr must have a trivial custom deleter, otherwise it corrupts the stack for pointers to automatic variables.

vsoftco
  • 55,410
  • 12
  • 139
  • 252
  • 3
    You should really link to [the most recent revision (N4282)](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4282.pdf), or [`std::experimental::observer_ptr`](http://en.cppreference.com/w/cpp/experimental/observer_ptr). Also, `reference_wrapper` is an option if the pointer should never be null. – T.C. Jan 02 '16 at 16:51
  • @T.C. Btw, do you know any implementation that has ``? I'm trying g++5.3 and clang++3.7, but the header is not implemented. – vsoftco Jan 02 '16 at 18:25
  • Might a `weak_ptr` help? In combo with the requisite `shared_ptr` and dynamic allocation it's probably too heavy here, but worth noting? – Lightness Races in Orbit Jan 02 '16 at 20:34
  • @LightnessRacesinOrbit Perhaps, although I'm not sure how would you use it with a pointer to an automatic variable? AFAIK, you'd need to construct it from a `shared_ptr`, and the latter will corrupt the stack at destruction. Do you have something else in mind? – vsoftco Jan 02 '16 at 20:42
  • @LightnessRacesinOrbit Ohh I see, missed the "dynamic allocation" part. – vsoftco Jan 02 '16 at 20:44
  • Indeed you did. Don't worry, that's not a `new` problem. – Lightness Races in Orbit Jan 02 '16 at 20:44
  • @LightnessRacesinOrbit :)) Good one. – vsoftco Jan 02 '16 at 20:45
  • 1
    @vsoftco: Thanks. I did have a different joke at first, but it was rubbish so I `delete`d it. – Lightness Races in Orbit Jan 02 '16 at 20:46
  • @LightnessRacesinOrbit Added a remark about the `weak_ptr`. – vsoftco Jan 02 '16 at 21:10
  • 1
    @vsoftco: That's great. And thanks for putting up with my `weak` jokes. – Lightness Races in Orbit Jan 02 '16 at 21:10
  • 1
    @LightnessRacesinOrbit Even `weak` jokes are allowed to be `shared`. – Hatted Rooster Jan 02 '16 at 21:22
  • So why exactly do we want to replace a normal pointer with some smart pointer implementation that does exactly nothing? I really don't see any reason for not just using a normal pointer if we don't own the pointer anyhow. – Voo Jan 02 '16 at 22:47
  • 1
    @Voo So e.g. you (or a client of your library) doesn't delete it by mistake. And in a sense you make it clear that the pointer is non owning. The abstract of the paper I've linked mentions: *"As such, it is intended as a near drop-in replacement for raw pointer types, with the advantage that, as a vocabulary type, it indicates its intended use without need for detailed analysis by code readers"*. – vsoftco Jan 02 '16 at 22:48
  • @LightnessRacesinOrbit You don't have to `make` excuses for your `observ`ations. – edmz Jan 02 '16 at 22:49
  • 1
    @vsoftco Fair enough. In my own code I consider any raw pointer non-owning by now, but I can see the advantages of codifying that. – Voo Jan 02 '16 at 23:03
  • The custom-deleter `unique_ptr` seems like a terrible idea to me. That would suggest to the reader that there are ownership semantics when in fact there are not. – M.M Jan 26 '16 at 21:53
  • @M.M Yeah it's probably not the best, I guess that's why observer ptr will be introduced. An alternative is to write a template alias with a different (more explicit) name or your own wrapper. – vsoftco Jan 26 '16 at 23:01
3

Using a raw pointer is perfectly ok here as you don't intend to let the pointer have ownership of the resource pointed to.

Felix Glas
  • 15,065
  • 7
  • 53
  • 82
1

The problem with a raw pointer is that there's no way to tell if it still points to a valid object. Fortunately, std::shared_ptr has an aliasing constructor that you can use to effectively make a std::weak_ptr to a class member with automatic storage duration. Example:

#include <iostream>
#include <memory>

using namespace std;

struct A {
    int x;
};

void PrintValue(weak_ptr<int> wp) {
    if (auto sp = wp.lock())
        cout << *sp << endl;
    else
        cout << "Object is expired." << endl;
}

int main() {

    shared_ptr<A> a(new A);
    a->x = 42;
    weak_ptr<int> wpInt (shared_ptr<int>(a, &a->x));

    PrintValue(wpInt);
    a.reset();  //a->x has been destroyed, wpInt no longer points to a valid int
    PrintValue(wpInt);

    return 0;
}

Prints:

42

Object is expired.

The main benefit to this approach is that the weak_ptr does not prevent the object from going out of scope and being deleted, but at the same time it can safely detect when the object is no longer valid. The downsides are the increased overhead of the smart pointer, and the fact that you ultimately need a shared_ptr to an object. I.e. you can't do this exclusively with objects allocated on the stack.

Community
  • 1
  • 1
Carlton
  • 4,217
  • 2
  • 24
  • 40
0

If by "better approach" you mean "safer approach", then yes, I've implemented a "non-owning" smart pointer here: https://github.com/duneroadrunner/SaferCPlusPlus. (Shameless plug alert, but I think it's relevant here.) So your code would look like this:

#include "mseregistered.h"
...

class foo{
public:
    mse::TRegisteredPointer<bar> pntr;
};

int main(){
    mse::TRegisteredObj<bar> a;
    foo b;
    b.pntr=&a;
}

TRegisteredPointer is "smarter" than raw pointers in that it knows when the target gets destroyed. For example:

int main(){
    foo b;
    bar c;
    {
        mse::TRegisteredObj<bar> a;
        b.pntr = &a;
        c = *(b.pntr);
    }
    try {
        c = *(b.pntr);
    } catch(...) {
        // b.pntr "knows" that the object it was pointing to has been deleted so it throws an exception. 
    };
}

TRegisteredPointer generally has lower performance cost than say, std::shared_ptr. Much lower when you have the opportunity to allocate the target object on the stack. It's still fairly new though and not well documented yet, but the library includes commented examples of it's use (in the file "msetl_example.cpp", the bottom half).

The library also provides TRegisteredPointerForLegacy, which is somewhat slower than TRegisteredPointer but can be used as a drop-in substitute for raw pointers in almost any situation. (In particular it can be used before the target type is completely defined, which is not the case with TRegisteredPointer.)

In terms of the sentiment of your question, I think it's valid. By now C++ programmers should at least have the option of avoiding unnecessary risk of invalid memory access. Raw pointers can be a valid option too, but I think it depends on the context. If it's a complex piece of software where security is more important than performance, a safer alternative might be better.

Noah
  • 41
  • 3
-2

Simply allocate the object dynamically and use a shared_ptr. Yes, it will actually delete the thing, but only if it's the last one with a reference. Further, it prevents others from deleting it, too. This is exactly the right thing to do, both to avoid memory leaks and dangling pointers. Also check out the related weap_ptr, which you could perhaps use to your advantage, too, if the lifetime requirements for the pointee are different.

Ulrich Eckhardt
  • 16,572
  • 3
  • 28
  • 55
  • 2
    That's not correct. If the pointer is to an automatic variable, then you end up corrupting the stack. – vsoftco Jan 02 '16 at 17:04
  • Good point, I wasn't clear about this part. I'd suggest reading the instructions in any case, to avoid common pitfalls like that. – Ulrich Eckhardt Jan 02 '16 at 17:13
  • 1
    I agree with conceptions (Urlich and others) in this part: C++ has too much memory models (for big applications, general computing), agree this "overdesign" (some kind) can be reduced for example using allocated data part from static / allocated / etc smart pointer. On the contrary: low hardware projects need use more aggressive code. BTW stack is high limited resource on many uC – Jacek Cz Jan 02 '16 at 17:29