2

What is a safe way to accessing member variables through a shared object in C++?

In the code below, I make a shared variable and then a pointer to a member variable of it. However, the use_count remains unchanged and when I reset the shared variable the original variable is reset but not the pointer to member.

In other words, I could introduce some bugs by using b. The object it is pointing to shouldn't exist anymore.

#include <iostream> #include <memory> using namespace std; struct A { int y; A(int x) { y = x; } }; int main() { auto a = make_shared<A>(1); cout << "a count: " << a.use_count() << endl; //prints a count: 1 auto b = a->y; cout << "a count: " << a.use_count() << endl; //still prints a count: 1 a.reset(); cout << "a value: " << a << endl; //prints a value: 0 cout << "b value: " << b << endl; //prints b value: 1 return 0; } 
3
  • 1
    Perhaps you could use a combination of shared_ptr<A> and pointer-to-member such as A::*y, depending on your use case Commented Jan 13, 2018 at 2:45
  • If you are trying to protect against concurrent access to the same member from different threads, you could protect the member with a std::mutex Commented Jan 13, 2018 at 2:59
  • 2
    You probably want to use shared_ptr's aliasing constructor: std::shared_ptr<int>(a, &a->y); Commented Jan 13, 2018 at 4:14

4 Answers 4

6
auto b = a->y; 

This will copy the value of y, so it's not a pointer to y, it's only a copy.

Sign up to request clarification or add additional context in comments.

2 Comments

Yeah you're right. I think what I really wanted was auto b = &(a->y); and then *b in the cout statement.
cplusplus.com/reference/memory/shared_ptr/reset "Additionally, a call to this function has the same side effects as if shared_ptr's destructor was called before its value changed (including the deletion of the managed object if this shared_ptr was unique)" So you will print destroyed object, and this is UB.
2
auto a = make_shared<A>(1); auto b = std::shared_ptr<int>(a, &a->y); 

b is a shared pointer to y that does what you want.

This is known as the "aliasing constructor" of shared ptr. It is intended for exactly this purpose; having a shared pointer into part of a larger shared ptr.

You can use it for lots of insane and fancy things on top of it. Be careful when using it, as you can get strange states like empty non-null shared ptrs, and non-empty null shared ptrs. But if you use it "normally" like the above, it will work fine.

What you did

auto a = make_shared<A>(1); auto b = a->y; 

simply copies y into b, no pointers are involved.

1 Comment

Thanks. This will return use_count() of 2.
1

What is a safe way to accessing member variables through a shared object in C++?

You are already doing it. The code you showed is safe.

In the code below, I make a shared variable and then a pointer to a member variable of it.

No, you don't. A::y is an int. In the auto b = a->y; statement, auto deduces to int, not to int& or int*. So, you are creating a new int whose value is a copy of y. There is no pointer to y.

If you wanted something like that, you would have needed to use one of these instead:

auto &b = a->y; ... cout << "b value: " << b << endl; 

auto *b = &(a->y); // or just: auto b = ... ... cout << "b value: " << *b << endl; 

However, the use_count remains unchanged

Correct, because you are not assigning a to another instance of shared_ptr<A>. That is what use_count represents - the number of shared_ptr objects that are sharing the same object in memory.

when I reset the shared variable the original variable is reset but not the pointer to member.

Of course, because b is independent of a and is not a pointer to anything related to a.

In other words, I could introduce some bugs by using b.

No, because b is not linked to a, so resetting a has no effect on b.

Now, if you did manage to accomplish what you are asking for (making b be a pointer to y), it is your responsibility to make sure the A object that y was accessed from remains valid in memory until you are done using b, otherwise dereferencing b will be undefined behavior. The STL won't save you from doing something stupid behind its back, like accessing an A object's member via a raw pointer after the object has been destroyed.

1 Comment

This is a great answer. Yes, I had meant auto b = &(a->y)
0

The moral of the story is don't hand out references to objects that may die before the reference. Design with every object's life-span in mind. One solution may be to copy the std::shared_ptr to make sure the reference is always pointing at the object another solution might be to pass a std::weak_ptr when you don't need the object to still be valid but you need to know so you don't try to use it. Only pass out pointers and references when you know the object will outlive them.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.