There have been tons of questions asked about passing by reference or pointer, and when to use pointers.
My understanding of the subject so far is the following rules:
Always try to pass by reference
Pass by pointer (only use pointers) if you must
In my case, I must use pointers in order to retain polymorphic behaviours as I am storing the passed object into a vector for later use (it is an 'add' method).
See: C++ Overridden method not getting called
I have read:
shared_ptr by reference or by value? which talks about passing shared_ptrs specifically
Passing shared pointers as arguments which tells me that I should only pass by shared_ptr if I am trying to transfer ownership, but later goes on to say that I should then pass by reference
Should I use std::shared pointer to pass a pointer? which pretty much tells me what the previous question told me, but in the context of a unique_ptr
So my question is this:
If I am trying to pass a pointer already contained in a shared_ptr to add to a vector, should I
pass a reference to the shared_ptr to be added into the vector (because the other method is unwieldy)
or
use shared_ptr::get to get the actual pointer, pass that pointer, re-wrap it using shared_ptr::reset, and then add it to the vector? (because I should only pass smart pointers if I'm transferring ownership)
Code:
//method definition
void addToVector(shared_ptr<Object>& obj) {
myVector.push_back(obj);
}
//call
shared_ptr<Object> myObj = make_shared<Object>();
addToVector(myObj);
or
//method definition
void addToVector(Object* obj) {
shared_ptr<Object> toAdd;
toAdd.reset(obj);
myVector.push_back(toAdd);
}
//call
shared_ptr<Object> myObj = make_shared<Object>();
addToVector(myObj.get());
I must use pointers in order to retain polymorphic behaviours as I am storing the passed object into a vector for later use
If you store the pointed object in vector, then you don't retain polymorphic behaviours.
Should I ... use shared_ptr::get to get the actual pointer, pass that pointer, re-wrap it using shared_ptr::reset, and then add it to the vector?
No. A shared pointer may not take ownership of the pointer that is already owned by another shared pointer. This would have undefined behaviour.
(because I should only pass smart pointers if I'm transferring ownership)
If you intend to store a shared pointer to the object, then you are transferring (sharing) the ownership. If that is your intention, then pass a const reference to the shared pointer, as described in the linked answer.
If you don't intend to share the ownership, then storing a shared pointer is not what you should do. You may want to store a reference wrapper, bare pointer, or a weak pointer instead. How you should pass the reference to the function, will depend on what you choose to do with it.
The second example is undefined behavior, so cannot be considered as a valid approach at all:
void addToVector(Object* obj) {
shared_ptr<Object> toAdd;
toAdd.reset(obj); // n.b. could just use shared_ptr(obj) ctor
myVector.push_back(toAdd);
}
shared_ptr<Object> myObj = make_shared<Object>();
addToVector(myObj.get()); // UB
What happens is that myObj owns its referent, then you use get() to form a raw pointer to that referent, then you create a new shared_ptr with the same referent in addToVector(). Now you have two smart pointers which refer to the same object but the two smart pointers don't know about each other, so will each destroy the object, which is double-free, which is undefined behavior.
Related
How do I use the move assignment operator when working with raw pointers.
Is there any other way than doing something like:
void Function(T* dest)
{
T* src = LoadT();
(*dest) = std::move(*src);
delete src;
}
Your move is fine. The object pointed to by src will be moved into the object pointed to by dest.
About your updated code example, the version with Function:
If your LoadT() returns a raw pointer to an object allocated with new, that does not get stored somewhere else and later deleted, you will have a memory leak.
When you std::move something, you move the contents of that object, the object itself remains alive, only "empty"/in whatever state you leave it after moving.
If you return a pointer to an object that is owned by someone else and will be cleaned up somehow beyond the code that's seen here, you could make that explicit by changing your pointers to references - that way you will explicitly specify that: a) the pointers are guaranteed to not be null; b) that there should be no worries about deleting the object you get from LoadT, since a reference can't have ownership of that object.
Unless you reference the objects in a temporary variable you are out of luck. Technically (not sure if illegal) you are allowed to provide your own specialization of move in which you can hide that behavior. But at one point or another, you have to dereference those pointers.
You want to have the pointed-to-objects moved onto one another, then you are already doing that in the correct/standard/only way, that is de-reference the pointer and then move *src onto *dest.
That being said, you interface is semanticly 'problematic'. A pointer to a function is by convention taken to mean non-owning and optional. Ownership seems not to be the issue here, but optional is. Why use a pointer when you can use a reference ? and moreover why use a reference when you can just use return value ?
In effect you could simplify to:
T Function()
{
return LoadT();
}
Then let users of the function decide how to best use T. If T is polymorphic you could return std::unique_ptr<T> instead.
First time I am using smart pointers in my project. While using unique_ptr, I got some doubts regarding unique_ptr and raw pointer combination. And the way unique_ptr works internally.
Could some one please explain/answer based on my understanding as mentioned below, so that I can go ahead and use the smart pointers.
Below is the example:
class A
{
public:
void show()
{
cout<<"A::show()"<<endl;
}
};
int main()
{
unique_ptr<A> p1 (new A);
p1 -> show();
// returns the memory address of p1
cout << p1.get();
retrun 0;
}
From the above example,
When creating unique_ptr object "p1" we are providing raw pointer. Internally, unique_ptr constructor will initialize the unique_ptr with the raw pointer. Is my understanding correct?
As per the unique_ptr definition, "The pointer is exclusively owned by one object or a resource".
Based on the above statement, in our scenario, "raw pointer" is exclusively
owned by the unique_ptr object "p1". Am I correct?
And also after the statement, cout << p1.get(); (In the above sample program) as it is going out of scope, internally, the destructor of the unique_ptr called and it deletes the associated raw pointer. Is my understanding correct?
Finally, once deletes the associated raw pointer is the unique_ptr object will become empty?
When creating unique_ptr object "p1" we are providing raw pointer. Internally, unique_ptr constructor will initialize the unique_ptr with the raw pointer. Is my understanding correct?
Yes. The unique pointer will hold the same address.
As per the unique_ptr definition, "The pointer is exclusively owned by one object or a resource".
Based on the above statement, in our scenario, "raw pointer" is exclusively owned by the unique_ptr object "p1". Am I correct?
Yes. The only reference, the one that owns the resource and will free it, is the unique pointer. Note however that it's not the pointer that's owned, but the object it points at. The unique_ptr didn't take ownership of the raw pointer, it took ownership of the object (the resource) that is at the address the raw pointer provided.
And also after the statement, cout << p1.get(); (In the above sample program) as it is going out of scope, internally, the destructor of the unique_ptr called and it deletes the associated raw pointer. Is my understanding correct?
Yes. The unique ptr will cause the deletion of its internal raw pointer when it goes out of scope.
Finally, once deletes the associated raw pointer is the unique_ptr object will become empty?
Doesn't have to. Since the deletion happens when the unique_ptr object itself is being destroyed, there is no real need to "empty" it. It's about to go out of existence anyway, so its value is immaterial.
Preamble
In C++11 there is std::shared_ptr + std::weak_ptr combo. Despite being very useful, it has a nasty issue: you cannot easily construct shared_ptr from a raw pointer. As a result of this flaw, such smart pointers usually become "viral": people start to completely avoid raw pointers and references, and use exclusively shared_ptr and weak_ptr smart pointers all over the code. Because there is no way to pass a raw reference into a function expecting a smart pointer.
On the other hand, there is boost::intrusive_ptr. It is equivalent to std::shared_ptr and can easily be constructed from raw pointer, because reference counter is contained within the object. Unfortunately, there is no weak_ptr companion to it, so there is no way to have non-owning references which you could check for being invalid. In fact, some believe that weak companion for intrusive_ptr is impossible.
Now, there is std::enable_shared_from_this, which embeds a weak_ptr directly into your class, so that you could construct shared_ptr from pointer to object. But there is small limitation (at least one shared_ptr must exist), and it still does not allow the obvious syntax: std::shared_ptr(pObject).
Also, there is a std::make_shared, which allocates reference counters and the user's object in a single memory chunk. This is very close to the concept of intrusive_ptr, but the user's object can be destroyed independently of the reference counting block. Also, this concept has an inevitable drawback: the whole memory block (which can be large) is deallocated only when all weak_ptr-s are gone.
Question
The main question is: how to create a pair of shared_ptr/weak_ptr, which would have the benefits of both std::shared_ptr/std::weak_ptr and boost::intrusive_ptr?
In particular:
shared_ptr models shared ownership over the object, i.e. the object is destroyed exactly when the last shared_ptr pointing to it is destroyed.
weak_ptr does not model ownership over the object, and it can be used to solve the circular dependency problem.
weak_ptr can be checked for being valid: it is valid when there exists a shared_ptr pointing to the object.
shared_ptr can be constructed from a valid weak_ptr.
weak_ptr can be constructed from a valid raw pointer to the object. Raw pointer is valid if there exists at least one weak_ptr still pointing to that object. Constructing weak_ptr from invalid pointer results in undefined behavior.
The whole smart pointer system should be cast-friendly, like the abovementioned existing systems.
It is OK for being intrusive, i.e. asking the user to inherit once from given base class. Holding the object's memory when the object is already destroyed is also OK. Thread safety is very good to have (unless being too inefficient), but solutions without it are also interesting. It is OK to allocate several chunks of memory per object, though having one memory chunk per object is preferred.
Points 1-4 and 6 are already modelled by shared_ptr/weak_ptr.
Point 5 makes no sense. If lifetime is shared, then there is no valid object if a weak_ptr exists but a shared_ptr does not. Any raw pointer would be an invalid pointer. The lifetime of the object has ended. The object is no more.
A weak_ptr does not keep the object alive, it keeps the control block alive. A shared_ptr keeps both the control block and the controlled object alive.
If you don't want to "waste" memory by combining the control block with the controlled object, don't call make_shared.
If you don't want shared_ptr<X> to be passed virally into functions, don't pass it. Pass a reference or const reference to the X. You only need to mention shared_ptr in the argument list if you intend on managing the lifetime in the function. If you simply want to perform operations on what the shared_ptr is pointing at, pass *p or *p.get() and accept a [const] reference.
Override new on the object to allocate a control block before the instance of the object.
This is pseudo-intrusive. Conversion to from raw pointer is possible, because of the known offset. The object can be destroyed without a problem.
The reference counting block holds a strong and weak count, and a function object to destroy the object.
Downside: it doesn't work polymorphically very well.
Imagine we have:
struct A {int x;};
struct B {int y;};
struct C:B,A {int z;};
then we allocate a C this way.
C* c = new C{};
and store it in an A*:
A* a = c;
We then pass this to a smart-pointer-to-A. It expects the control block to be immediately before the address a points to, but because B exists before A in the inheritance graph of C, there is an instance of B there instead.
That seems less than ideal.
So we cheat. We again replace new. But it instead registers the pointer value and size with a registry somewhere. There we store the weak/strong pointer counts (etc).
We rely on a linear address space and class layout. When we have a pointer p, we simply look for whose range of address it is in. Then we know the strong/weak counts.
This one has horrible performance in general, especially multi-threaded, and relies upon undefined behavior (pointer comparisons for pointers not pointing to the same object, or less order in such cases).
In theory, it is possible to implement intrusive version of shared_ptr and weak_ptr, but it might be unsafe due to C++ language limitations.
Two reference counters (strong and weak) are stored in the base class RefCounters of the managed object. Any smart pointer (either shared or weak) contains a single pointer to the managed object. Shared pointers own the object itself, and shared + weak pointers together own the memory block of the object. So when the last shared pointer is gone, object is destroyed, but its memory block remains alive as long as there is at least one weak pointer to it. Casting pointers works as expected, given that all the involved types are still inherited from the RefCounted class.
Unfortunately, in C++ it is usually forbidden to work with members of object after the object is destroyed, although most implementations should allow doing that without problems. More details about legibility of the approach can be found in this question.
Here is the base class required for the smart pointers to work:
struct RefCounters {
size_t strong_cnt;
size_t weak_cnt;
};
struct RefCounted : public RefCounters {
virtual ~RefCounted() {}
};
Here is a part of shared pointer definition (shows how object is destroyed and memory chunk is deallocated):
template<class T> class SharedPtr {
static_assert(std::is_base_of<RefCounted, T>::value);
T *ptr;
RefCounters *Counter() const {
RefCounters *base = ptr;
return base;
}
void DestroyObject() {
ptr->~T();
}
void DeallocateMemory() {
RefCounted *base = ptr;
operator delete(base);
}
public:
~SharedPtr() {
if (ptr) {
if (--Counter()->strong_cnt == 0) {
DestroyObject();
if (Counter()->weak_cnt == 0)
DeallocateMemory();
}
}
}
...
};
Full code with sample is available here.
struct Temp
{
CString one;
CString two;
};
class Foo
{
public:
Temp obj;
void somewhere();
}
void Foo::somewhere()
{
void* pData = static_cast<void*>(&obj);
OwnMethod(pData); // void OwnMethod(void*);
}
The question is:
Should I create obj on heap or this situation isn't dangerous (passing local class objects pointer)?
If OwnMethod(pData) stores the pointer somwhere for later use, that later use is not possible anymore, once the object on which Foo::somewhere() is called is destroyed.
If OwnMethod(pData) only access the pointed to data, you are safe.
The member variable will last as long as the Foo object, so the pointer will be valid during the call to OwnMethod.
If that function stores a copy of the pointer somewhere, and something else uses that pointer later, then there is a danger that it might be accessed after the Foo (and therefore the pointer's target) have been destroyed. There are various ways to prevent that; as you say, one is to dynamically allocate the object, and then transfer or share ownership when it's passed to OwnMethod. Smart pointers, such as std::unique_ptr and std::shared_ptr, are a very good way to track ownership of dynamic objects.
Wow, a lot of issues.
A complex object should't be passed to anything taking a void*.
Who wrote OwnMethod?
Why doesn't it take a pointer of type Foo*?
In fact why doesn't it take a reference of type Foo&?
If OwnMethod() may be required to accept objects of several different types then it should take a base class pointer or reference and use polymorphism.
However as far as the lifetime arguments go - obj will exist as long as the wrapping class does, so if the pointer is not used beyond the scope of OwnMethod this is ok. If OwnMethod causes the pointer to be stored elsewhere beyond Foo's lifetime then you have an issue, and maybe obj should be allocated on the heap. (And it might not even be appropriate for it to be a member of Foo at all.)
When using shared_ptr, should I just use the shared_ptr declaration once or declare shared_ptr everywhere I pass it?
So in the function where I new up the instance I wrap it in a shared_ptr but when I return it from the function I could also return a shared_ptr or, using the get() on the shared_ptr, just return a normal pointer.
So my question is, should I just use shared_ptr<myType> when I new the instance and then pass normal pointers around or should I be passing shared_ptr<myType> everywhere?
Creating a shared_ptr doesn't imbue magical powers on its pointee object. The magic is all in the shared_ptr — and its copies — itself. If you stop using it, you lose your reference counting; worse, because you used it at some point, the object will be automatically deleted when you don't expect it.
The whole point of having shared_ptr is that you know your object won't get destroyed when you're still using it somewhere.
In the following:
T* foo() {
shared_ptr<T> sp(new T());
return sp.get();
// ^ the only shared_ptr<T> referencing the obj is dead;
// obj is deleted;
// returned pointer invalid before you can even do anything with it
}
your pointer is immediately invalid.
There may well be circumstances in which you extract a raw pointer, but these should be rare. If you are in a function where you know you don't need the reference counting, then just pass the shared_ptr in by reference.