Even though my code compiles fine, this is something that has been bugging me, and I wasn't able to find an answer on stackoverflow. The following generic constructor is one way to pass a shared_ptr to a class instance in the constructor.
MyClass {
MyClass(const std::shared_ptr<const T>& pt);
std::shared_ptr<const T> pt_; //EDITED: Removed & typo
};
MyClass::MyClass(const std::shared_ptr<const T>& pt)
: pt_(pt)
{ }
This compiles fine. My question is the following: In my understanding, declaring a parameter const like this:
void myfunc(const T& t)
promises not to change t. However, by copying the shared_ptr pt to pt_, am I not effectively increasing the use count of the shared_ptr pt, thereby violating the supposed const-ness?
This might be a fundamental misunderstanding of shared_ptrs on my side?
(For anybody reading this looking to implement it, note that this might be a better implementation)
::Ptr is typedef-ed to std::shared_ptr<MSG>, which is a shared pointer to a message, and ::ConstPtr is typedef-ed to std::shared_ptr<MSG const>, which is a shared pointer to a const message. but what does & means? If msg is already a pointer, why do we take the address of msg? Or does that & means passing by reference?
Why not be orthogonal, and put the const after everywhere (since you have to put it after in some cases). You are right. shared_ptr<const T> p; is similar to const T * p; (or, equivalently, T const * p; ), that is, the pointed object is const whereas const shared_ptr<T> p; is similar to T* const p; which means that p is const.
You can add copy constructor by Person (const Person&) = default; It seems that you want to have 'deep copy' rather than a 'shared' object. The smart-pointer acts like the raw-pointer, so the shared resource would change if any change is made through one of the pointer pointing to it.
The shared_ptr object is a class which manages the object it wraps and "points to", and the & is to pass the shared_ptr object by reference. So, we are passing a message by a reference to a smart pointer which points to it--yeah, it's kind of a double-layered approach.
One of the members that std::shared_prt<>
must have is the old fashioned copy constructor:
shared_ptr(const shared_ptr& r) noexcept;
The standard says (C++11 20.7.2.2.1/18 "shared_ptr constructors") that "if r is empty, constructs an empty shared_ptr object; otherwise, constructs a shared_ptr object that shares ownership with r".
The standard doesn't make mention of how "shares ownership with r" might be accomplished though a const
reference. Some options might be:
mutable
shared_ptr
object - they might be a separate set of objects that might be obtained through a pointer, for example.A shared pointer is conceptually laid out as follows:
The shared_ptr contains a pointer to the object and also a pointer to a control block. It is the control block that controls the lifetime of the pointee, not the shared_ptr object itself, which is no more than a wrapper and some code to notify the control block that the number of references has been increased or reduced. It is the control block that stores the reference count, the deleter and the pointer to the original interface of the pointee (so the deleter can delete against the correct interface even if there has been a pointer cast).
* shared_ptr object *
| pointer to object | ---------------> object
| pointer to control block |----+ +> (possibly different interface
| | but still same object)
| |
* control block * <----------+ |
| reference count | |
| deleter | |
| pointer to object | --------------+
Since a shared_ptr's memory looks something like this:
template<class T>
struct shared_ptr {
T* ptr;
control_block* pctrl;
};
It should start to become obvious that even if the shared_ptr is const, taking a copy does not require any mutation of the shared_ptr's internal state. The mutation happens in the control block, which is pointed to by the shared_ptr.
Thus the contract is not broken. Just as if you declared
T* const p;
modifying p
itself is not possible, but modifying (*p)
is perfectly reasonable.
The simple answer to your question is no, because the reference count is not stored in the shared pointer instance, but in an external object which takes care of keeping the reference count. When you copy construct a shared_ptr, the reference count is added in the external object. Check out this lecture by Stephen T. Lavavej which explains the implementation
const
, in an interface, means whatever it wants to mean. Its meaning should be documented.
Usually, it means "some subset of ny state will not change". For shared ptr, the subset of state that cannot change is "what I point to".
The count can change. The contents can change.
In the C++ std library, const
can be interpreted to mean "thread safe" -- because if const
operations are thread safe, and you put them in std
containers, the std
container in turn has thread-safe const operations.
By thread safe, I do not mean sync -- I mean two different threads, both doing const stuff, is a-ok. If a thread is doing non-const stuff, all bets are off.
This permits simple reader-writer lock logic.
And as add/remove ref is indeed thread safe, while reseating ptr is not...
My question is the following: In my understanding, declaring a parameter const like this...promises not to change t.
Not entirely true. The promise is not to change any of its observable state...most of the time. There are several ways in which a const object can "change":
It has mutable variables -- these are meant to change under const constraints but design methodology says these should be rare and shouldn't be observable. One more common use for them is to cache something that is expensive to calculate. So you have a const function get
that does a massive calculation to return a value--you want this to be optimized so you create a cache. The cache has to change during the get
call but in reality get
always returns the same thing so nobody could observe that the state of the object has changed.
It has non-const pointers or references to other objects. In these cases, aggregation, it's not the object that changes but something else. This is what happens in the case of shared_ptr
, which has a pointer to a shared reference counting object that actually holds the value of the pointer. It's unintuitive at first because the reported state of such an object can change, but it's actually not the object itself that changed. Design methodology here is on a case-by-case basis, but the language in no way protects you unless you declare the pointers as pointer to const.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With