Inside the copy constructor of shared_ptr - c++

I have some confusion about the shared_ptr copy constructor. Please consider the following 2 lines:
It is a "constant" reference to a shared_ptr object, that is passed to the copy constructor so that another shared_ptr object is initialized.
The copy constructor is supposed to also increment a member data - "reference counter" - which is also shared among all shared_ptr objects, due to the fact that it is a reference/pointer to some integer telling each shared_ptr object how many of them are still alive.
But, if the copy constructor attempts to increment the reference counting member data, does it not "hit" the const-ness of the shared_ptr passed by reference? Or, does the copy constructor internally use the const_cast operator to temporarily remove the const-ness of the argument?

The phenomenon you're experiencing is not special to the shared pointer. Here's a typical primeval example:
struct Foo
{
int * p;
Foo() : p(new int(1)) { }
};
void f(Foo const & x) // <-- const...?!?
{
*x.p = 12; // ...but this is fine!
}
It is true that x.p has type int * const inside f, but it is not an int const * const! In other words, you cannot change x.p, but you can change *x.p.
This is essentially what's going on in the shared pointer copy constructor (where *p takes the role of the reference counter).

Although the other answers are correct, it may not be immediately apparent how they apply. What we have is something like this:
template <class T>
struct shared_ptr_internal {
T *data;
size_t refs;
};
template <class T>
class shared_ptr {
shared_ptr_internal<T> *ptr;
public:
shared_ptr(shared_ptr const &p) {
ptr = p->ptr;
++(ptr->refs);
}
// ...
};
The important point here is that the shared_ptr just contains a pointer to the structure that contains the reference count. The fact that the shared_ptr itself is const doesn't affect the object it points at (what I've called shared_ptr_internal). As such, even when/if the shared_ptr itself is const, manipulating the reference count isn't a problem (and doesn't require a const_cast or mutable either).
I should probably add that in reality, you'd probably structure the code a bit differently than this -- in particular, you'd normally put more (all?) of the code to manipulate the reference count into the shared_ptr_internal (or whatever you decide to call it) itself, instead of messing with those in the parent shared_ptr class.
You'll also typically support weak_ptrs. To do this, you have a second reference count for the number of weak_ptrs that point to the same shared_ptr_internal object. You destroy the final pointee object when the shared_ptr reference count goes to 0, but only destroy the shared_ptr_internal object when both the shared_ptr and weak_ptr reference counts go to 0.

It uses an internal pointer which doesn't inherit the contests of the argument, like:
(*const_ref.member)++;
Is valid.

the pointer is constant, but not the value pointed to.

Wow, what an eye opener this has all been! Thanks to everyone that I have been able to pin down the source of confusion to the fact that I always assumed the following ("a" contains the address of "b") were all equivalent.
int const *a = &b; // option1
const int *a = &b; // option2
int * const a = &b; // option3
But I was wrong! Only the first two options are equivalent. The third is totally different.
With option1 or option2, "a" can point to anything it wants but cannot change the contents of what it points to.
With option3, once decided what "a" points to, it cannot point to anything else. But it is free to change the contents of what it is pointing to. So, it makes sense that shared_ptr uses option3.

Related

Shared pointer passed by ref, copy and moved into a vector

I am trying to understand which of the following usage of shared pointer makes more sense as it gets inserted into a vector.
bar takes a const reference of a shared pointer vs foo that takes a copy. In both the cases, the passed-in parameter gets moved into a vector. The interesting part is the use_count of a in the caller remains 2 for foo and bar both which implies the the vector stores a "copy"?
Like if a shared_ptr is passed by a reference, its count doesn't increment. As soon as it's "moved" into a vector, it does. Does that mean vector isn't storing the reference to an original object a?
class A
{
std::vector<std::shared_ptr<int>> _vec;
public:
void bar(const std::shared_ptr<int>& ptr)
{
_vec.emplace_back(std::move(ptr));
}
void foo(std::shared_ptr<int> ptr)
{
_vec.emplace_back(std::move(ptr));
}
};
int main()
{
auto sp = std::make_shared<int>();
A a;
// a.foo(sp); // use_count = 2
a.bar(sp); // use_count = 2
}
You're passing the shared_ptr to bar by reference to const. That means that the original shared_ptr can't be modified via that reference.
Moving from a share_ptr requires modifying the moved-from shared_ptr to set it to point to nothing.
See the issue? bar can't move from ptr, so it instead ends up copying it into the vector. ptr/sp remains valid and continues to point to the int that you allocated in main and a._vec also holds a shared_ptr to that same int. Thus use_count must be 2.
If you want to actually move from sp then you should change bar to accept some sort of mutable reference. Really you should make it accept an rvalue reference though, since a.bar(sp) causing sp to become invalid would violate most programmers' expectations:
class A
{
std::vector<std::shared_ptr<int>> _vec;
public:
void bar(std::shared_ptr<int>&& ptr)
{
_vec.emplace_back(std::move(ptr));
}
};
int main()
{
auto sp = std::make_shared<int>();
A a;
a.bar(std::move(sp));
// Here sp.use_count() is 0 because it was moved from
// a._vec.back().use_count() will be 1 though
}
Demo
This limits the caller to always moving their shared_ptr into A. Simply accepting the parameter by value as you do in foo will likely result in no measurable performance difference when the caller wants to give up ownership and provides greater flexibility when they don't.
In both cases, the shared pointer being passed to foo and bar is being moved into the vector. This means that the vector takes ownership of the dynamically allocated memory that the shared pointer was managing, and the shared pointer in the caller is no longer managing any memory.
The difference between foo and bar is that foo takes a copy of the shared pointer, while bar takes a const reference to the shared pointer. Since foo takes a copy, the shared pointer's reference count is incremented by one when it is passed to foo. This means that the reference count will be 2 after foo is called.
In contrast, bar takes a const reference to the shared pointer, so the reference count is not incremented when it is passed to bar. This means that the reference count will remain at 1 after bar is called.
Overall, the use of bar is more efficient in this case because it does not require an extra increment of the reference count, but the difference in performance is likely to be small in practice. It is more important to choose the approach that is more readable and maintainable for your specific use case.

Can smart pointers be implicitly used as pointers?

Are smart pointers considered as pointers? And thus can they implicitly used as pointers?
Let's say I have the following class:
class MyClass {
//...
std::shared_ptr<AnotherClass> foo() { /*whatever*/ };
void bar(AnotherClass* a) { /*whatever too*/ };
//...
}
Then can I use MyClass the following way?
// m is an instance of MyClass
m.bar(m.foo());
No they can't be used interchangable. You would get a compiler error in your example. But you can always get the raw pointer by shared_ptr::get().
NO! It would be a terrible API. Yes, you could easily implement it within shared_ptr, but just because you could doesn't mean you should.
Why is it such a bad idea? The plain-pointer-based interface of bar doesn't retain an instance of the shared pointer. If bar happens to store the raw pointer somewhere and then exit, there's nothing that guarantees that the pointer it had stored won't become dangling in the future. The only way to guarantee that would be to retain an instance of the shared pointer, not the raw pointer (that's the whole point of shared_ptr!).
It gets worse: the following code is undefined behavior if foo() returns a pointer instance that had only one reference when foo() returned (e.g. if foo is a simple factory of new objects):
AnotherClass *ptr = m.foo().get();
// The shared_ptr instance returned by foo() is destroyed at this point
m.bar(ptr); // undefined behavior: ptr is likely a dangling pointer here
Here are the options; consider those listed earlier first before considering their successors.
If bar(AnotherClass *) is an external API, then you need to wrap it in a safe way, i.e. the code that would have called Original::bar should be calling MyWrapped::bar, and the wrapper should do whatever lifetime management is necessary. Suppose that there is startUsing(AnotherClass *) and finishUsing(AnotherClass *), and the code expects the pointer to remain valid between startUsing and finishUsing. Your wrapper would be:
class WithUsing {
std::unique_ptr<AnotherClass> owner; /* or shared_ptr if the ownership is shared */
std::shared_ptr<User> user;
public:
WithUsing(std::unique_ptr<AnotherClass> owner, std::Shared_ptr<User> user) :
owner(std::move(owner)), user(std::move(user)) {
user.startUsing(owner.get());
}
void bar() const {
user.bar(owner.get());
}
~WithUsing() {
user.finishUsing(owner.get());
}
};
You would then use WithUsing as a handle to the User object, and any uses would be done through that handle, ensuring the existence of the object.
If AnotherClass is copyable and is very cheap to copy (e.g. it consists of a pointer or two), then pass it by value:
void bar(AnotherClass)
If the implementation of bar doesn't need to change the value, it can be defined to take a const-value (the declaration can be without the const as it doesn't matter there):
void bar(const AnotherClass a) { ... }
If bar doesn't store a pointer, then don't pass it a pointer: pass a const reference by default, or a non-const reference if necessary.
void bar(const AnotherClass &a);
void bar_modifies(AnotherClass &a);
If it makes sense to invoke bar with "no object" (a.k.a. "null"), then:
If passing AnotherClass by value is OK, then use std::optional:
void bar(std::optional<AnotherClass> a);
Otherwise, if AnotherClass takes ownership, passing unique_ptr works fine since it can be null.
Otherwise, passing shared_ptr works fine since it can be null.
If foo() creates a new object (vs. returning an object that exists already), it should be returning unique_ptr anyway, not a shared_ptr. Factory functions should be returning unique pointers: that's idiomatic C++. Doing otherwise is confusing, since returning a shared_ptr is meant to express existing shared ownership.
std::unique_ptr<AnotherClass> foo();
If bar should take ownership of the value, then it should be accepting a unique pointer - that's the idiom for "I'm taking over managing the lifetime of that object":
void bar(std::unique_ptr<const AnotherClass> a);
void bar_modifies(std::unique_ptr<AnotherClass> a);
If bar should retain shared ownership, then it should be taking shared_ptr, and you will be immediately converting the unique_ptr returned from foo() to a shared one:
struct MyClass {
std::unique_ptr<AnotherClass> foo();
void bar(std::shared_ptr<const AnotherClass> a);
void bar_modifies(std::shared_ptr<AnotherClass> a);
};
void test() {
MyClass m;
std::shared_ptr<AnotherClass> p{foo()};
m.bar(p);
}
shared_ptr(const Type) and shared_ptr(Type) will share the ownership,
they provide a constant view and a modifiable view of the object, respectively. shared_ptr<Foo> is also convertible to shared_ptr<const Foo> (but not the other way round, you'd use const_pointer_cast for that (with caution). You should always default to accessing objects as constants, and only working with non-constant types when there's an explicit need for it.
If a method doesn't modify something, make it self-document that fact by having it accept a reference/pointer to const something instead.
Smart pointers are used to make sure that an object is deleted if it is no longer used (referenced).
Smart pointer are there to manage lifetime of the pointer they own/share.
You can think of a wrapper that has a pointer inside. So the answer is no. However you can access to the pointer they own via get() method.
Please note that it is not so difficult to make dangling pointers if you use get method, so if you use it be extra cautious.

C++ NULL pointers and const correctness

I read that it is good practice to do a check in the destructors of classes after deletion for pointer data members as follows:
if( 0 != m_pPointer)
{
delete m_pPointer;
m_pPointer= 0;
}
However, I found out that this prevents you to declare const pointers as data members as follows:
Type* const m_pPointer;
Isn't assigning NULL to pointers(as in my example above) a barrier for const-correctness?
What is the best way to do? Keep everything const and stop assigning NULL to the deleted pointer or declaring non-const pointers even though their address never changes?
This is bad practice for the following reasons:
Setting a pointer to null in the destructor may mask double destruction problem. Good practise is to detect problems as early as possible.
Checking a pointer for null before deleteing it only adds unnecessary code. delete handles null pointers by doing nothing. Good practice is to minimize the amount of code.
Deleting a null pointer is guaranteed safe, so that null check is pointless.
If a class has a member that is a const pointer to a non-const object then you're saying the pointer value WILL NOT change within the lifetime of the wrapping object - that being the case you should only do this in the case where the object pointed to will live as long or longer than the wrapping object and the wrapping object will never want to point to a different object.
The fact that you have this issue simply means you've used a const pointer in the wrong place. You claim that in your case the pointer value never changes, but in your example it obviously does - it changes to null.
The "best way to do" is:
class foo {
std::unique_ptr<bar> m_pPointer;
public:
foo(std::unique_ptr<bar> pPointer)
: m_pPointer{std::move(pPointer)} {}
};
or for const,
class foo {
const std::unique_ptr<bar> m_pPointer;
public:
foo(std::unique_ptr<bar> pPointer)
: m_pPointer{std::move(pPointer)} {}
};
No new, no delete, no destructor.
A weird situation can be caused when you link a static lib with a global or static object from two different shared libs (on Linux) which later be linked to the same executable.
Each shared lib object insert call to constructor and destructor, so you'll have one object and two calls for constructor and destructor for the same object (actually you'll have 2 objects mapped to the same address).
You'll probably find the problem when your app crash in the 2nd destructor.
if you NULL it you'll never know that there was a problem at all.
for your question: except for the above issue, I think you should distinct two types of pointers:
See the class below:
class A{
obj *x, *y;
A(){
x = new obj;
y = NULL
}
~A(){
delete x;
if(y)delete y; // the `if` here will save the calling and returning run time when NULL.
}
void RecicleX(){
delete x;
x = new obj;
}
void InitY(){
assert(y==NULL); //illegal to call init when already
y = new obj;
}
void TermY(){
assert(y); //illegal to call term when already inited
delete y;
y = NULL; //prevent crush in dtor if called after...
}
};
x is always exists, so no need to check it, and no need to null it. y may exists and may not, so I think you should null it after deletion.
(You maybe will want also to know the current state, like for assert)

Is there a case where element selection by reference and element selection through pointer operation are both valid?

My background is in more managed languages (C#, python) but I am becoming more experienced in C/C++. I am familiar with why the selection by reference (.) and selection through pointer operation (->) operators are different. In all cases I have encountered, if you use the incorrect one, it will result in a compile error. If that is the case, they why were they not made into one operator? Is there a case where using either on the same object results in different, meaningful and useful results?
This question inspired by this answer:
Is this right way to call a function in c++?
In C++ you can overload the ->-operator, which is used in pretty much all smart pointer implementations. However, some of those also have their own methods, i.e. to release a reference.
struct test {
int x;
};
std::shared_ptr<int> ptr(new test);
// Write to member x of the allocated object
ptr->x = 3;
// Reset the shared pointer to point to a different object.
// If there are no further shared_ptrs pointing to the previously allocated one,
// it is deleted.
ptr.reset(new test)
Additionally, it would be quite messy for the compiler to resolve operator-. for something like multiple-level pointers, i.e. test*** ptr. With your logic, ptr.x, (*ptr).x, (**ptr).x and (***ptr).x would all be the same.
You cannot apply -> to a reference to a basic type and you cannot apply . to a pointer, but you can apply both to a user-defined type and they will have different meanings. The simplest example is a smart pointer, like std::shared_ptr:
struct A { int x; };
std::shared_ptr<A> p(new A);
p->x = 10;
p.reset();
Is there a case where element selection by reference and element selection through pointer operation are both valid?
Since you can overload operator->() in C++, you can actually arrive at situations where you can use -> and . interchangeably on the same object. You can even engineer things so that you get a different result, as per this example:
#include <iostream>
struct Bar
{
void hello() const { std::cout << "Bar!!!\n"; }
};
struct FooBar
{
Bar bar;
void hello() const { std::cout << "FooBar!!!\n"; }
const Bar* operator->() const {return &bar; }
};
int main()
{
FooBar fb;
fb->hello();
fb.hello();
}
Of course, in real code you would never do something as crazy as this (although I have seen this kind of thing in "production" code).
the short answer would be a smart pointer
you can access the smart pointer class arguments using the "." (if you make your own smart pointer class you can extract from there for instance the current reference count) while you would use the "->" operator to access whatever is being stored using the smart pointer.

Indirectly calling non-const function on a const object

Given the following code:
class foo;
foo* instance = NULL;
class foo
{
public:
explicit foo(int j)
: i(j)
{
instance = this;
}
void inc()
{
++i;
}
private:
int i;
};
Is the following using defined behavior?
const foo f(0);
int main()
{
instance->inc();
}
I'm asking because I'm using a class registry, and as I don't directly modify f it would be nice to make it const, but then later on f is modified indirectly by the registry.
EDIT: By defined behavior I mean: Is the object placed into some special memory location which can only be written to once? Read-only memory is out of the question, at least until constexpr of C++1x. Constant primitive types for instance, are (often) placed into read-only memory, and doing a const_cast on it may result in undefined behavior, for instance:
int main()
{
const int i = 42;
const_cast<int&>(i) = 0; // UB
}
Yes, it is undefined behavior, as per 7.1.5.1/4:
Except that any class member declared mutable (7.1.1) can be modified, any attempt to modify a const object during its lifetime (3.8) results in undefined behavior.
Note that object's lifetime begins when the constructor call has completed (3.8/1).
This may be one of the rare cases where the not very known mutable keyword could be used:
mutable int i;
i can now be changed even if the object is const. It's used when logically the object doesn't change, but in reality it does.
For example:
class SomeClass
{
// ....
void DoSomething() { mMutex.lock(); ...; }
mutable Mutex mMutex;
}
In DoSomething() the object doesn't logically change and yet mMutex has to change in order to lock it. So it makes sense to make it mutable, otherwise no instance of SomeClass could be const (assuming you lock the muetx for every operation).
If you define a const instance of the object, then cast away the const-ness, and modify the contents of the object, you get undefined behavior.
From the sound of things, what you want is exactly the opposite: create a non-const instance of the object, then return a const pointer to that object to (most of) the clients, while the "owner" retains a non-const pointer to the object so it can modify members as it sees fit.
You'd typically manage a situation like this by defining the class with a private ctor, so most clients can't create objects of the type. The class will then declare the owner class as a friend, so it can use the private ctor and/or a static member function to create instances (or often only one instance) of the object. The owner class then passes out pointers (or references) to const objects for clients to use. You need neither a mutable member nor to cast away constness, because the owner, which has the "right" to modify the object, always has a non-const pointer (or, again, reference) to the object. Its clients receive only const pointers/references, preventing modification.
Calling a non-const (by declaration) member function on a const object is not illegal per se. You can use whatever method you wish to work around the compiler restrictions: either an explicit const_cast or a trick with constructor as in your example.
However, the behavior is only defined as long as the member function you are calling does not make an attempt to actually physically modify the object (i.e. modify a non-mutable member of the constant object). Once it makes an attempt to perform a modification, the behavior becomes undefined. In your case, method inc modifies the object, meaning that in your example the behavior is undefined.
Just calling the method, again, is perfectly legal.
It's hard to tell the intent with these arbitrary names. If i is intended as just a use counter, and it isn't really considered part of the data, then it is perfectly appropriate to declare it as mutable int i; Then the const-ness of an instance is not violated when i is modified. On the other hand, if i is meaningful data in the space being modeled, then that would be a very bad thing to do.
Separately from that, though, your example is a bit of a mess for what you seem to be asking. foo* instance = NULL; is effectively (if confusingly) using a NULL as a numeric zero and initializing instance, which is not const; then you separately initialize f, which is const, but never reference it.
Under GCC, at least, your constructor should be explicit foo(int j) with the word int.
However, it's perfectly fine to have two pointers to the same value, one const and the other not.
Why dont you make use of const cast ?
Any reason to make object as const eventhough its state is not constant?
Also make following change :
explicit foo(int j = 0) : i(j)
{ instance = this; }