C++ rvalue shared_ptr and rvalue weak_ptr - c++

std::shared_ptr<std::string> test() {
return std::make_shared<std::string>("sdsd");
}
cout << *test() << endl;
The above code works. Can someone please let me know where is the "sdsd" string stored. I was expecting it to error out as rvalue is a temporary object. Where is the rvalue copied to and stored in?
With weak_ptr
std::weak_ptr<std::string> test() {
return std::make_shared<std::string>("sdsd");
}
cout << *test().lock() << endl;
Interestingly the above code errors out. What's the difference?

In
std::shared_ptr<std::string> test() {
return std::make_shared<std::string>("sdsd");
}
the constructed shared_ptr is returned and lives on as a temporary variable long enough to be used by the caller before going out of scope and freeing the string allocated by make_shared.
The "sdsd" string is stored in dynamic storage owned by the returned shared_ptr.
In
std::weak_ptr<std::string> test() {
return std::make_shared<std::string>("sdsd");
}
the shared_ptr wasn't returned and went out of scope, taking the string allocated by make_shared with it. Since this was the only existing copy of the shared_ptr this leaves the returned temporary weak_ptr variable connected to an expired share_ptr. If you test the return value of lock you'll see it's a default-constructed shared_ptr holding a null pointer.
Documentation for std::weak_ptr::lock.
The "sdsd" string isn't stored anywhere after test returns. It departed when the shared_ptr that owned it went out of scope.

The above code works. Can someone please let me know where is the "sdsd" string stored. I was expecting it to error out as rvalue is a temporary object. Where is the rvalue copied to and stored in?
The memory was allocated in the call to make_shared.
Interestingly the above code errors out. What's the difference?
The difference is that the lock operation on a weak_ptr can fail if there does not exist at least one shared_ptr to the object. That's the purpose of weak_ptr -- to allow the object to be freed and access it only if it still exists.

Related

State of underlying resource when a shared_ptr is created from the raw pointer?

This link about shared pointers states that
You can pass a shared_ptr to another function in the following ways:
...
Pass the underlying pointer or a reference to the underlying object. This enables the callee to use the object, but doesn't enable it to share ownership or extend the lifetime. If the callee creates a shared_ptr from the raw pointer, the new shared_ptr is independent from the original, and doesn't control the underlying resource. Use this option when the contract between the caller and callee clearly specifies that the caller retains ownership of the shared_ptr lifetime.
Similar case which uses raw pointers to show better practice of initializing shared pointers using make_shared and new clearly states that if initialization from raw pointers occurs, it would cause to deletion of the resource twice.
I am also trying to run code below:
void f(int* ptr)
{
shared_ptr<int> sptr(ptr);
cout << "sptr in f: " << sptr.get() << endl;
}
int main()
{
auto sp1 = make_shared<int>(1);
cout << "sp1 in main: " << sp1.get() << endl;
f(sp1.get());
return 0;
}
and get following error.
sp1 in main: 0x6000016f5138
sptr in f: 0x6000016f5138
a.out(12083,0x119097600) malloc: *** error for object 0x6000016f5138: pointer being freed was not allocated
a.out(12083,0x119097600) malloc: *** set a breakpoint in malloc_error_break to debug
zsh: abort ./a.out
My question is what is meant in the given link by
If the callee creates a shared_ptr from the raw pointer, the new shared_ptr is independent from the original, and doesn't control the underlying resource.
when it is not the case from my example code output and also similar stack overflow post link above.
The paragraph is weirdly worded. If you remove this sentence:
If the callee creates a shared_ptr from the raw pointer, the new shared_ptr is independent from the original, and doesn't control the underlying resource.
It's fine. Indeed, talking about what would happen if the callee creates a new shared_ptr is something of a non-sequitur in this context.
This sentence is intended to be a warning not to create a shared_ptr from random pointers you get passed as arguments. The new shared_ptr is "independent from the original," but the rest of the sentence would be better stated as "and both of them will try to destroy the underlying resource". It's not so much that it doesn't "control" it; it's that it doesn't do so correctly.

How to assign the shared_ptr object to the function of type raw pointer using C++

I am creating the shared_ptr in a function and returning raw pointer from that function.
To get the underlying raw pointer from the shared_ptr I am using .get()
If i am using the raw pointer in a function and assigning to the function of type raw pointer it is working without any issue.
But if i create the shared_ptr and while returning, it is calling the destructor and deleting the memory assigned to the object and crashing.
How to assign the shared_ptr object to the function of type raw pointer?
CMyClass::CMyClass()
{
}
CMyClass::~CMyClass()
{
}
CMyClass* CreateClassInstance()
{
std::shared_ptr<CMyClass> l_MyClassInterface = std::make_shared<CMyClass>();
return l_MyClassInterface.get();
}
CMyClass* CreateClassInstance()
{
CMyClass* l_MyClassInterface = new CMyClass();
return l_MyClassInterface;
}
auto l_pMyClassInterface = CreateClassInstance();
Yes, what #user17732522 said.
In the code as written, l_MyClassInterface is going out of scope when your first version of CreateClassInstance returns, taking your newly created object with it. When you return a shared_ptr, as opposed to the pointer returned by get(), the mechanism that it uses to keep track of the reference count for your object kicks in and stops that from happening. (Well, in principle. In practise, copy elision usually / always ensures that the shared_ptr created by make_shared is returned directly to the caller, but that's a detail.)
The other (inferior) solution would be to return the pointer returned by new directly and assign it to a shared_ptr in the caller. But that is error-prone, and not recommended.

The mechanism of shared_ptr

I accidentally found this issue on C++, but totally had no idea how it was happened, please check the code snippet below:
int main() {
int *aptr = new int(20); //Declare an integer space with 20, and aptr points to it
shared_ptr<int> a(aptr); //Declare a shared_ptr to points where aptr points
stringstream ss;
ss << a;
const char *chstr = ss.str().c_str(); // Save the address as string
void *address;
sscanf(chstr, "%p", (void **)&address); // Convert string back to pointer
a = NULL; // Free the shared pointer
cout << *reinterpret_cast<int*>(address) << endl; // Output: 0, 20 has gone
return 0;
}
Is there anybody could tell me why the address has been freed ?
I didn't manipulate the original integer pointer "aptr", but somehow it's space has disappeared, because of the shared_ptr ?
I'd like to know how it was happend, thank you all !
The whole purpose behind std::shared_ptr is to take ownership of the dynamically-allocated object. Once std::shared_ptr takes ownership of a dynamically-allocated object, via either std::make_shared or by directly assigning a pointer to it, std::shared_ptr owns it, lock stock and barrel. Owning it means automatically deleteing the dynamically scoped object when the last reference to the dynamically-scoped object goes out of scope. That's what the shared_ptr is for, and nothing else.
a = NULL;
This used shared_ptr's assignment operator to replace the shared_ptr-owned pointer(*). Since the original dynamically-scoped object that the shared_ptr owns has no other reference, the shared_ptr deletes the original pointer that was used to construct it.
The fact that you still have the original, native pointer to the int is irrelevant. shared_ptr doesn't care. The whole thing gets deleted.
If you don't want shared_ptr to delete whatever you newed, and use a native pointer for, either don't use shared_ptr, or make sure one is still around as long as you still need to use a native pointer to the underlying object.
(*) This actually implicitly constructs another shared_ptr, and uses the regular assignment operator.
This code does not compile in g++ 4.2.1 (but as Sam mentioned, it complies with version 6):
Georgioss-MacBook-Pro:~ gsamaras$ g++ -Wall main.cpp
main.cpp:17:11: error: no viable overloaded '='
a = NULL; // Free the shared pointer
~ ^ ~~~~
By the way, you should use shared_ptr::reset:
In all other cases, the shared_ptr acquires ownership of p with a use
count of 1, and -optionally- with del and/or alloc as deleter and
allocator, respectively.
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).
Also check string::c_str. You are saving the address of a temporary string, which is the start of something bad.

What Happens to a weak_ptr when Its shared_ptr is Destroyed?

It seems that a weak_ptr somehow just knows when the shared_ptr it references has been destroyed. How is that? Is there a constant link maintained or something?
Take the following code for example:
weak_ptr<int> test() {
shared_ptr<int> foo{new int};
return foo;
}
int main() {
auto foo = test();
cout << foo.expired() << endl;
}
I would have expected a segfault when the weak_ptr<int> goes to check on the state of the shared_ptr<int> but there isn't one. The weak_ptr<int> correctly identifies the memory as deallocated. How does it know?
A std::shared_ptr is created using two pieces of memory:
A resource block: This holds the pointer to the actual underlying data, e.g. 'int*'
A control block: This holds information specific to a shared_ptr, for example reference counts.
(Sometimes these are allocated in a single chunk of memory for efficiency, see std::make_shared)
The control block also stores reference counts for weak_ptr. It will not be deallocated until the last weak_ptr goes out of scope (the weak pointer reference count drops to zero).
So a weak_ptr will know that it's expired because it has access to this control block, and it can check to see what the reference count is for a shared_ptr

What's the difference between raw pointer and weak_ptr?

As in title. This question probably already has an answer but I failed to find one.
The fundamental conceptual difference between a naked pointer and a weak_ptr is that if the object pointed to is destroyed, the naked pointer won't tell you about it. This is called a dangling pointer: a pointer to an object that doesn't exist. They're generally hard to track down.
The weak_ptr will. In order to use a weak_ptr, you must first convert it into a shared_ptr. And if that shared_ptr doesn't point to anything, then the object was deleted.
For example:
#include <iostream>
#include <memory>
std::weak_ptr<int> wp;
void test()
{
auto spt = wp.lock(); // Has to be copied into a shared_ptr before usage
if (spt) {
std::cout << *spt << "\n";
} else {
std::cout << "wp is expired\n";
}
}
int main()
{
{
auto sp = std::make_shared<int>(42);
wp = sp;
test();
}
test();
}
Output
42
wp is expired
A raw pointer is (at least normally) simply an address. You can't tell anything about what it points at from the pointer itself.
A weak_ptr is always associated with a shared_ptr, so we probably need to start with a shared_ptr to make any sense of a weak_ptr.
A shared_ptr is reference counted, so it keeps track of how many references (pointers) to an object exist, and automatically destroys the object when no more references to that object exist.
As I already said, a weak_ptr is associated with a shared_ptr. Unlike a shared_ptr, the existence of a weak_ptr does not increment the reference count for the pointee object. To use a weak_ptr, you must first convert it to a shared_ptr. If the current reference count is positive, that will succeed, and converting the weak_ptr to a shared_ptr will increment the reference count to signify that the converted pointer is a "real" reference to the object. If, on the other hand, the reference count is already zero (meaning the pointee object has already been destroyed) the attempt to convert the weak_ptr to a shared_ptr will simply fail.
A shared_ptr means shared ownership of the pointee object. The pointee object will remain in existence as long as at least one shared_ptr to that object exists, but as soon as the last shared_ptr to the object is destroyed, so will the pointee object.
A weak_ptr means non-owning access to the pointee object. It allows access if the object exists. If the object has been destroyed, it tells you that the pointee object no longer exists rather than attempting to access the destroyed object.