Reputation: 20264
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;
}
Upvotes: 15
Views: 6682
Reputation: 4297
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.
Upvotes: 1
Reputation: 41
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.
Upvotes: 0
Reputation: 56557
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.
Upvotes: 23
Reputation: 17415
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.
Upvotes: -2
Reputation: 15524
Using a raw pointer is perfectly ok here as you don't intend to let the pointer have ownership of the resource pointed to.
Upvotes: 3