I'm fairly new to smart pointers and I couldn't understand the result of the code I attached below. Basically the reference count and the raw pointer it self of a shared_ptr object is changed after it is passed to another unique_ptr object.
So the code goes like this.
typedef shared_ptr<int> sp;
class bar {
public:
const sp& x; // line 3: if change this line to "const sp x;" then it's fine
bar(const sp xx) : x(xx) {
cout << "bar: count=" << x.use_count() << "; addr=" << x.get() << endl;
}
void test() {
cout << "test: count=" << x.use_count() << "; addr=" << x.get() << endl;
}
};
int main() {
auto p = make_shared<int>(0);
auto b = make_unique<bar>(p);
b->test();
}
If I compile and run the code, it prints out:
bar: count=2; addr=0x557c8b02dec0
test: count=1224736736; addr=0x7ffdb5cbd5c0
So the ref count and pointer are both changed. And it looks like some uninitialized value.
But if I change line 3 to const sp x; then both values look normal:
bar: count=3; addr=0x5590f026eec0
test: count=2; addr=0x5590f026eec0
Sorry about the bad namings since it's a quick minimum case I came up with to address my question. I feel it has something to do with how unique_ptr works, but I'm not sure.
Can someone shed some light and help me understand why the code behaves like that?
Thanks
Here:
const sp& x;
bar(const sp xx) : x(xx)
You assign the shared pointer into a local variable, xx, and then bind the const reference x to that local variable. When the constructor concludes its run, that local variable goes out of scope, leaving you with a reference to Eldritch Horrors and undefined behavior.
To explain your observation regarding the removal of the &:
Removing the & prevents this because in that case you just assign the shared pointer straight into x which henceforth is not dependent on the local variable xx, and so is unaffected by its going out of scope.
To expand: another thing you could do instead, if what you wanted was to hold a reference, is to set xx to be a reference itself (i.e bar(const sp& xx) : x(xx)); in this case you'd be binding x to p.
Related
As far as I know, auto_ptr works on the concept of transfer of ownership. Also, once an auto pointer transfer its ownership to another auto pointer, it should not be able refer to the object it points to anymore. However, this is not the case I found as shown in the below program. Am I missing something? Please help.
#include <iostream>
#include <string>
#include <memory>
class A
{
public:
void display(){
std::cout << "Inside class A" << std::endl;
}
};
int main()
{
std::auto_ptr<A> p1(new A());
std::auto_ptr<A> p2;
std::cout << p1.get() << std::endl;
p2=p1;
std::cout << p2.get() << std::endl;
p2->display();
std::cout <<p1.get()<< std::endl; //Address of shows 0 as expected
p1->display(); //L1
std::auto_ptr<A> p3 = p1; //L2
std::cout << p3.get() << std::endl; //Address shows 0 as expected
p3->display();
return 0;
}
Output:
0x45a0620
0x45a0620
Inside class A
0
Inside class A
0
Inside class A
Line L1: How does this work, since p1 do not have the ownership anymore?
Line L2: How does this work, since p1 do not have the ownership anymore?
Your code doesn't show what you think it does.
This is straightforward Undefined Behaviour: the auto_pointer here is only obscuring the fact that your code reduces to:
A *a {nullptr};
a->display();
Consider method A::display - it isn't virtual, so is essentially a simple function whose name has class scope, and which receives through some mechanism an implicit this pointer to the object on which it was invoked.
Since the function address doesn't depend on the object pointer, your compiled has emitted code which calls the function successfully, just with this==nullptr.
If you display prints the value of this, or use a non-static data member of A inside the function, this should be apparent.
Finally, as NathanOliver pointed out, auto_pointer is deprecated anyway and for good reason.
I have a class, let's call it A. There are two subclasses of class A which are a and b.
I'm making a pointer of class A like this:
A *pointer;
At some point in the program I initialize the pointer like this:
pointer = new a();
At some other point, I run a function of class A:
pointer->function(&pointer);
This function is inside class A (so all subclasses have it). There is a chance that when this function is called, I want to change pointer to another subclass, here is what I tried:
void A::function(A **pointer)
{
if (something)
{
delete *pointer;
*pointer = new b();
}
}
Although this works, I'm really curious if this is good practice, I'm calling delete from inside the object and freeing the object itself, could this be undefined behavior and I got lucky it worked? Am I not understanding this right? Am I making this more complicated than it should be?
Yes, that's valid as long as you are careful. See more discussion at a question specifically about delete this.
However, as with other things in C++ which are valid as long as you are careful, you are better to find another solution that will be less prone to errors. I suggest you reworking you code into a function returning a new pointer, and having the old one automatically destroyed (via a smart pointer, for example).
Something along the lines:
struct A {
static std::shared_ptr<A> function(std::shared_ptr<A>& ptr, int x) {
if (x > 0)
return std::make_shared<A>(x);
else return ptr;
}
A(int _x): x(_x) {}
int x;
};
Note also I made function() to be static, as it anyway accepts the object as its first argument. See live on coliru.
In fact, I don't quite like this solution with shared_ptr, and if someone will give a better implementation of this approach, I'll be glad to know.
This code is valid ( for more information about correctness see this answer ).
But it's not a good practice, because other developer can miss nuance, that using one of member functions will lead to reconstruction of object.
It's better to explicitly reconstruct object than hide it in member function.
Or just use smart pointers.
As a design I don't like that a pointer suddenly points to another object (of a different type) when it is not clear that it happens. It could be argued that since OPs code passes &pointer it indicates that it may change. However, I prefer an assignment instead - I think that is more clear.
I would try something like this:
int uglyGlobal = 1; // don't try this at home... ;-)
class A
{
public:
int n;
A() {n = uglyGlobal++; cout << "A cons for #" << n << endl;}
virtual ~A() {cout << "A des for #" << n << endl;}
unique_ptr<A> function(int something, unique_ptr<A> ptr);
};
class a : public A
{
public:
a() {cout << "a cons" << endl;}
~a() override {cout << "a des" << endl;}
};
class b : public A
{
public:
b() {cout << "b cons" << endl;}
~b() override {cout << "b des" << endl;}
};
unique_ptr<A> A::function(int something, unique_ptr<A> ptr)
{
if (something == 0)
{
// Turn it into an A
return unique_ptr<A>(new A);
}
else if (something == 1)
{
// Turn it into an a
return unique_ptr<A>(new a);
}
else if (something == 2)
{
// Turn it into an b
return unique_ptr<A>(new b);
}
else
// Keep the current
return ptr;
}
int main()
{
cout << "Make A" << endl;
unique_ptr<A> x (new A);
cout << "1. call - turn A into a" << endl;
x = x->function(1, move(x));
cout << "2. call - turn a into b" << endl;
x = x->function(2, move(x));
cout << "3. call - turn b into another b" << endl;
x = x->function(2, move(x));
cout << "4. call - keep current b" << endl;
x = x->function(3, move(x));
cout << "Return from main" << endl;
return 0;
}
The output is:
Make A
A cons for #1
1. call - turn A into a
A cons for #2
a cons
A des for #1
2. call - turn a into b
A cons for #3
b cons
a des
A des for #2
3. call - turn b into another b
A cons for #4
b cons
b des
A des for #3
4. call - keep current b
Return from main
b des
A des for #4
In general, You must ensure that every execution path that call function doesn't have a stack frame for a method of A or derived class (this is invalid).
So, it is dangerous. In MFC api programming, this happens "normally" in the WM_NCDESTROY message handler. In it you do thing like delete this, but Windows ensure that WM_NCDESTROY is the last message sent to a window.
I suggest you to change a little the api of class A and use unique_ptr to handle the memory:
#include <memory>
class A
{
public:
std::unique_ptr<A> f()
{
return std::make_unique<A>();
}
};
int main()
{
auto p = std::make_unique<A>();
p = std::move(p->f());
return 0;
}
In this way, you move the destruction from inside f() to the assignment of p.
I would like to use parameter in C++ to store back whatever value/object.
In this example, I try to store the value from the global variable as a simplified example.
This code doesn't work,
int value = 20;
void returnPointer2(int* hello)
{
hello = &value;
}
// It changes nothing
int value2 = 100;
returnPointer2(&value2);
cout << value2 << endl;
as I needed double pointer.
void returnPointer3(int** hello)
{
*hello = &value;
}
int* vp2 = new int();
*vp2 = -30;
returnPointer3(&vp2);
cout << *vp2 << endl; // expects 20
I reminded of the reference, and I can use pointer reference to get the same result.
void returnPointer4(int* & hello)
{
cout << "value : " << value;
hello = &value;
}
int* vp3 = new int();
*vp3 = -130;
returnPointer4(vp3); // also expects 20, but much simpler to use
cout << "better : " << *vp3 << endl;
I tried with double &, and it compiles.
void returnPointer5(int&& hello)
{
cout << "value : " << value;
hello = value;
}
However, it doesn't compile with the input of integer variable.
int vp4 = 123;
returnPointer5(vp4); // also expects 20, but even more simpler to use
cout << "best : " << vp4 << endl;
This is an error message.
pointer_return.cpp:31:6: error: initializing argument 1 of 'void returnPointer5(int&&)'
void returnPointer5(int&& hello)
I happened to know about move, and it works with this code.
int vp4 = 123;
returnPointer5(move(vp4)); // also expects 20, but much simpler to see
cout << "best : " << vp4 << endl;
What's the magic/logic behind this move function?
There is a lot of stuff getting mixed in here, but to keep it simple I'll address your root question.
&& is nothing at all like **.
&& is an rvalue reference, while ** is a pointer to a pointer.
As a second point, you are declaring in your function name what you want to do: returnPointer4.
You want to have a pointer to an integer returned back. int*& is the correct syntax for having a reference to a pointer.
Reading over your question again, why don't you use the following:
int& returnGlobalReference() {
return value;
}
Then in your other function:
int& value2 = returnGlobalReference();
The first attempt makes the classic mistake of passing a pointer by value, modifying its address in the function and expecting what it points to to change.
As mentioned in the comments,
void returnPointer2(int* hello)
{
hello = &value; // don't do this, it only modifies what the
// pointer hello, which resides in the stack, points to
*hello = value; // do this instead. even though hello still resides in the
// stack, you're modifying the location that hello points to,
// which was your original intention
}
why do you want to pass pointers however? is the static variable not available when you call the function? (perhaps, different files?)
The magic of std::move is:
The actual declaration for std::move is somewhat more involved, but at its heart, it's just a static_cast to an rvalue reference.
Taken from here.
As Jeffery Thomas already said, a && is not a reference to a reference, but a reference to a rvalue.
Can someone tell why test(2) object is destroyed after test_method() call?
#include<iostream>
#include<string>
using namespace std;
class test
{
int n;
public:
test(int n) : n(n)
{
cout << "test: " << n << endl;
}
~test()
{
cout << "~test: " << n << endl;
}
test & test_method()
{
cout << "test_method: " << n << endl;
return *this;
}
};
int main(int argc, const char *argv[])
{
cout << "main start" << endl;
const test &test1 = test(1);
const test &test2 = test(2).test_method();
cout << "main end" << endl;
}
Output is:
main start
test: 1
test: 2
test_method: 2
~test: 2
main end
~test: 1
test(2).test_method() returns a reference, which is bound to test2, and then the object to which it refers is destroyed at the end of the full expression, since it is a temporary object. That should not be a surprise.
The real surprise is that test1 remains a valid reference, because it is directly bound to a temporary, and binding a temporary to a reference extends the lifetime of the temporary to that of the reference variable.
You only have to note that in the test(2) case, the temporary object isn't bound to anything. It's just used to invoke some member function, and then its job is done. It doesn't "babysit" member functions, or in other words, lifetime extension isn't transitive through all possible future references.
Here's a simple thought experiment why it would be impossible to actually have "arbitrary lifetime extension":
extern T & get_ref(T &);
{
T const & x = get_ref(T());
// stuff
// Is x still valid?
}
We have no idea if x remains valid beyond the first line. get_ref could be doing anything. If it's implemented as T & get_ref(T & x) { return x; }, we might hope for magic, but it could also be this:
namespace { T global; }
T & get_ref(T & unused) { return global; }
It's impossible to decide within the original translation unit whether anything needs to be extended or not. So the way the standard has it at present is that it's an entirely trivial, local decision, just made when looking at the reference declaration expression, what the lifetime of the temporary object in question should be.
Because the C++ standard requires this behavior. Give the object a name if you want it to persist. It will persist as long as the name.
Edit: You your example, test1 is the name that you gave to the first object, whereas the second object has obtained no name at all, and so it does not outlast evaluation of the expression.
In the below code, when I pass an unnamed A variable to the ctor of B, the variable is destructed after the line. According to this answer :
Temporary objects are destroyed at the
end of the full expression they're
part of. A full expression is an
expression that isn't a sub-expression
of some other expression. Usually this
means it ends at the ; (or ) for if, while, switch etc.)denoting the end
of the statement.
I get it but how can the class B know the value of its mamber_a variable, after it is destructed? I know that copy ctor of A is enver called. How is this possible?
#include <iostream>
using namespace std;
class A
{
int sign;
A();
const A & operator=(const A &);
public:
A(int x) : sign(x) {
cout << "A ctor : " << sign << endl;
}
void WriteA() const {
cout << sign << endl;
}
~A() {
cout << "A dtor : " << sign << endl;
}
A(const A &) {
cout << "A copied : " << sign << endl;
}
};
class B
{
int sign;
const A & member_a;
public:
B(const A & aa , int ww ) : sign (ww) ,member_a(aa) {
cout << "B ctor : " << sign << endl;
}
void WriteB() const {
cout << "Value of member_a :";
member_a.WriteA();
}
~B() {
cout << "B dtor : " << sign << endl;
}
};
int main() {
A a(10);
B b1(a,1);
b1.WriteB();
B b2(A(20),2);
b2.WriteB();
return 0;
}
The output is :
A ctor : 10
B ctor : 1
Value of member_a :10
A ctor : 20
B ctor : 2
A dtor : 20
Value of member_a :20 // Object A was destructed. Where does this 20 come from?
B dtor : 2
B dtor : 1
A dtor : 10
You have one of the tricky parts of C++
It is pure chance that member_a has the value 20. You are hitting what is refereed to as undefined behavior.
When a class retains a reference to an external object it is the responsibility of the programmer to make sure that the lifetime of the object lasts longer than the object being referred to. In this case when you call member_a.WriteA(); the a object has already been destroyed and thus you are accessing memory that may potentially not belong to you (in this case it just happens to be in a location nearby that has not been overridden (completely by chance)).
If you are going to retain a reference to an object. You can retain a const reference but sometimes it is best to make the parameter a normal reference so that you can not accidentally pass a temporary value (this does not always work as you may have to pass a const object by reference).
Using a reference to a destructed object is an "undefined behaviour".
In A's destructor, try setting "sign" to "-1" and see what happens. Odds are the call to "WriteB" will show that you have sent a message to a deceased object.
Now try putting a bunch of stack using code between the constructor of b2 and the call to b2.WriteB, for example a call to a subroutine. You will probably now find that the call prints something different.