I understand how to move an object, for example:
int a(5), b(6);
int c(std::move(a)); // move constructor
c = std::move(b); // move assignment
I understand the common implementation of a custom move constructor (which takes the ownership of a dynamic pointer and sets the moved-from pointer to nullptr.)
But I haven't found anything about moving a dynamically allocated pointer itself. I need confirmation that the following code is legal:
int *p(new int(42));
int val(std::move(*p));
delete p; // I think this is no longer necessary. Is it?
So, is it allowed to move a dynamic pointer?
std::move does not move anything. It merely casts an l-value reference to an r-value reference.
In the example you give, the pointer p has not moved anywhere.
BTW. please don't deal in raw pointers - use a std::unique_ptr instead.
This allocates memory for an int, initialises the int to the value of 42 and stores the address of that memory in p.
int *p(new int(42));
This casts the int pointed to by p to an int&& and then constructs the int val from that r-value reference. Since int is an integral type, a construction from an r-value (i.e. a move) is equivalent to a copy (this is mandated in the standard)
int val(std::move(*p));
Yes, this is still necessary.
delete p;// i think this is no longer necessary. is it ?
In your second example, your code essentially copy the value of one integer to anther. You are not moving the pointer, you are moving the integer the pointer points to.
int a = 42;
int b = std::move(a);
std::cout << a << " : " << b << std::endl; // prints 42 : 42
Moving an integer is copying them.
Even if you moved the pointer, the value of the pointer of copied:
int* a = new int{42};
int* b = std::move(a);
std::cout << *a << " : " << *b << std::endl; // prints 42 : 42
std::cout << std::boolalpha << (a == b) << std::endl; // prints true
So moving raw pointers are copying them too.
You will still need to delete the p pointer.
For every new, there's a delete.
If you don't want to delete it yourself, consider std::unique_ptr, which is aware about move semantics.
Built-in type such as raw pointer, int, floats are not aware of move semantics and moving them will simply copying them.
In your example the use of move semantics is trivial, since you are only using primitive types and raw pointers. std::move is really doing nothing.
Move semantics make the difference when your objects belong to a class that supports move operations (move constructor, move assignment, etc...), or interact with other classes/ functions that do so.
Tipically, after you have move from an object (which is not the result of std::move itself, but the result of the subsequent call), the object is still alive and in a valid state, although it has been deprived of any managed content. Distruction of the objects still remains to be done, it's still a living object.
Note that you rarely see move semantics with raw pointers. It is common to see it with automatic storage objects, or smart pointers. And that's just because is the scenario where move semantics play well with.
Related
I am a beginner to c++, I was learning the concept of shared_ptr.
I also understood that
Several shared_ptr objects may own the same object and the object is destroyed and its memory deallocated when either of the following happens:
1.the last remaining shared_ptr owning the object is destroyed;
2.the last remaining shared_ptr owning the object is assigned another pointer via operator= or reset().
But when I tried to execute a sample program
class Rectangle {
int length;
int breadth;
public:
Rectangle(int l, int b)
{
length = l;
breadth = b;
}
int area()
{
return length * breadth;
}
};
int main()
{
shared_ptr<Rectangle> P1(new Rectangle(10, 5));
cout << P1->area() << endl;
shared_ptr<Rectangle> P2;
P2 = P1; //how is this possible
// This'll print 50
cout << P2->area() << endl;
// This'll now not give an error,
cout << P1->area() << endl;
cout << P1.use_count() << endl;
return 0;
}
After "P2=P1" the memory allocated to P1 has to be deallocated right?
But still we can print P1->area().
Please explain how this is happening?
2.the last remaining shared_ptr owning the object is assigned another pointer via operator= or reset().
Yes.
After "P2=P1" the memory allocated to P1 has to be deallocated right?
No. It could happen to P2 if it pointed to something. Not P1.
The logic behind the rule (2) is that the assignment overwrites the value of the first operand, so the first operand will no longer point to what it used to. If it was the last pointer to something, then nothing will point to that anymore, and it can be deleted.
What you learned about destruction of shared pointers is correct. But here, you are not destroying P1. Instead, you are assigning P1 to P2. The implementation of shared_ptr has an overloaded copy assignment operator allowing for this operation and making it correct.
Through that overloaded implementation, P2 is now a shared pointer pointing to the same object as P1 - both pointers access the same object, so you are printing the same areas. Both of them exist in a valid state and as you see, the count of pointers that manage that Rectangle object is 2.
Defining the overloaded = is aligned with the concept of shared_ptr - there are multiple pointers pointing to (owing) the same object. If you want to see a contrasting implementation, look up the unique_ptr - this is a smart pointer that assumes only one pointer has an ownership of the object. It also has an overloaded assignment operator, but using it would invalidate P1 (to my knowledge it would set it to nullptr so it would still be in a valid state, just not pointing to anything). P2 would be the only owner of the Rectangle. Worth trying, for better understanding.
You can find more on the shared_ptr functionality here.
Hello so i am trying to understand what is going on under the hood.
int* puter;
puter = std::make_unique<int>(50).get();
if(puter) { std::cout << *puter << std::endl; }
At first i thought the puter should be a dangling ptr because temporary unique_ptr from make_unique would be destroyed along with the allocated resource. But that is not the case.
After a while i could understand that it might not happen if the unique_ptr resource was move assigned to puter.
But the real question is. When should i assume things will get move asigned from temporary? Is there a rule to it? Should i just use release method for assurance in such cases?
As others have mentioned, this actually triggers undefined behaviors.
Your original thought is exactly what happened
At first i thought the puter should be a dangling ptr because temporary unique_ptr from make_unique would be destroyed along with the allocated resource
Thus,
if(puter) { std::cout << *puter << std::endl; }
causes undefined behaviors -- You don't know what is there, different compilers and machines can have different results(e.g. can be 50, can be 0, can be nullptr, or some garbage values...)
But the real question is. When should i assume things will get move asigned from temporary? Is there a rule to it? Should i just use release method for assurance in such cases?
It depends if the type/class support move semantics(i.e. have move constructor/move assignment operator defined). If not, it's the same as copying.
In this case, int* is a primitive type, and primitive types do not have move constructor/assignment, so assign a temporary will not be move assign.
On the other hand, if you assign a temporary std::vector to another std::vector, move assignment will be called because std::vector class has a well-defined move assignment operator(and a move constructor). e.g.
std::vector<int> v;
v = std::vector<int>{1,2,3}; //RHS is a temporary object, so it will call the vector's move assignment operator
As I know std::move (same as static_cast<T&&>) casts variable to rvalue and assigns to lvalue, and because of this I think in following code:
int a = 1;
int b = static_cast<int&&>(a);
b and a have the same address, but in VS, it prints different addresses.
int a = 1;
int b = static_cast<int&&>(a);
cout << hex << &a << endl;
cout << hex << &b << endl;
If after this a still points to a different memory location, what is the benefit of using std::move in this case?
Just because you "move" them doesn't mean they will share the same address. Moving a value is a high level abstraction, with basic types like int moving and copying is completely the same, which is happening here. I suggest you read the excellent post on std::move to know what it does and what it's uses are.
No, b is its own object, which is copy initialized from an rvalue reference to another int. This is the same as just copying the referenced object.
Move semantics only shines when the "copying" can be preformed by resource stealing (since we know the other objects storage is about to go, anyway).
For a type like an integer, it's still a plain copy.
There is no benefit of using std::move on an int. In your example you are basically copying the value from a to b.
Move semantics is only meaningfull on resources where you want to transfer ownership, e.g. dynamically allocated memory. Take the std::unique_ptr as an example.
auto ptr = std::make_unique<int>(1);
auto ptrCopy = ptr; // copy will not work compilation error.
auto ptrMove = std::move(ptr);
In the above example ptrMove has taken over the ownership of ptr and ptr is now empty.
When you use std::move in C++ you do not move the object itself, you move the value of the object or its contents. So its address does not change.
Moving is no different from copying with an int. But for a complex type with internal pointers to allocated memory, that memory can be transferred without copying using a std::move (assuming it has been designed to respond to std::move).
Why would I use get() with *, instead of just calling *?
Consider the following code:
auto_ptr<int> p (new int);
*p = 100;
cout << "p points to " << *p << '\n'; //100
auto_ptr<int> p (new int);
*p.get() = 100;
cout << "p points to " << *p.get() << '\n'; //100
Result is exactly the same. Is get() more secure?
Practically no difference.
In case of *p, the overloaded operator* (defined by auto_ptr) is invoked which returns the reference to the underlying object (after dereferencing it — which is done by the member function). In the latter case, however, p.get() returns the underlying pointer which you dereference yourself.
I hope that answers your question. Now I'd advise you to avoid using std::auto_ptr, as it is badly designed — it has even been deprecated, in preference to other smart pointers such as std::unique_ptr and std::shared_ptr (along with std::weak_ptr).
*p calls auto_ptr::operator*, which dereferences the managed pointer.
*p.get first calls method auto_ptr::get, which returns the managed pointer, which is then dereferenced by operator *.
These will provide exactly the same result once executed: the managed pointer is dereferenced, and there will be no additional checking when using get.
Note that auto_ptr is deprecated since C++11. It is dangerous because ownership of the pointer is transfered when copying:
std::auto_ptr<int> p(new int(42));
{
std::auto_ptr<int> copy_of_p(p); // ownership of *p is transfered here
} // copy_of_p is destroyed, and deletes its owned pointer
// p is now a dangling pointer
To avoid the problem, you had to "manage the managed pointers":
std::auto_ptr<int> p(new int(42));
{
std::auto_ptr<int> copy_of_p(p); // ownership of *p is transfered here
// ...
p = copy_of_p; // p gets back ownership
} // copy_of_p is destroyed, but doesn't delete pointer owned by p
// p is still valid
Use unique_ptr or shared_ptr instead.
There is no effective difference. operator* is defined to return *get() (cppreference).
You should consider using unique_ptr in auto_ptr's stead. The latter has been removed from the current C++ standard and has very non-intuitive yank-on-copy behaviour.
Don't use get() on a smart pointer (regardless of auto_, unique_, shared_, or otherwise). This returns a naked pointer that isn't under the control of an RAII class. This opens up various possibilities of giving the pointer to another RAII class (causing double-deletion later), or the caller doing something silly like deleting the pointer (again causing double-deletion). Just dereference the smart pointer.
PS: For experts only: Yes, there are other legitimate reasons for extracting the raw pointer from a smart pointer. But by not using get() in general, the times that it is used becomes a red flag for "there's something funky going on here, pay attention!".
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
What is The Rule of Three?
People say that if you need a destructor then you actually need an overloaded operator=
struct longlife{ };
class z
{
z(){};
~z(){ for( auto it=hold.begin();it!=hold.end() ++it ) delete(*it); };
vector<longlife*> hold;
};
Suppose all pointer inserted on hold were newheap allocated, why is anything else besides a deconstructor needed on this example?
By anything else I mean things like,
z& operator=( const z&ref )
{
hold = ref.hold;
return *this;
}
Would:
z a;
a.hold.push_back( heap_item );
z a2;
a2 = a;
Cause memory leak? Sometimes it's hard to understand why the rule of three is a rule
Not only is the assignment operator required, you also need to implement a copy constructor. Otherwise the compiler will provide default implementations that will result in both copies (after assignment / copy construction) containing pointers to the same longlife instances. Destructors of both copies will then delete these instances leading to undefined behavior.
z a;
a.hold.push_back( heap_item );
z a2;
a2 = a;
Both a.hold[0] and a2.hold[0] contain a pointer to the same heap_item; thus causing double deletion during destruction.
The easy way to avoid having to implement the assignment operator and copy constructor is to use a smart pointer to hold the longlife instances in the vector.
std::vector<std::unique_ptr<longlife>> hold;
Now there's no need to even write a destructor for your class.
For C++03, your options are to use std::tr1::shared_ptr (or boost::shared_ptr) instead of unique_ptr or use boost::ptr_vector (of course, this is also an option for C++11) instead of std::vector.
Because without an assignment operator and a copy constructor you may end up with multiple hold vectors pointing to the same heap item, resulting in undefined behavior upon destruction:
z firstZ;
if (somethingIsTrue) {
z otherZ = firstZ;
// play with otherZ...
// now otherZ gets destructed, along with longlife's of the firstZ
}
// now it's time to destroy the firstZ, but its longlife's are long gone!
Of course you would not have this problem had you used a vector of objects or a vector of "smart pointers", rather than a vector of "plain old" pointers.
See the Rule of Three for more information.
It would cause a double delete (and crash) on the destructor of a or a2 (whichever was destroyed second) because the default assignment constructor would do a binary copy of the memory state of hold. So each object a and a2 would end up deleting the exact same memory.
From your comments:
#Xeo, I understand what the rule of three is, the question is mostly
why is it a rule
Consider what happens here:
z& operator=( const z&ref )
{
hold = ref.hold;
return *this;
}
Lets say you have an instance of z:
z myz;
myz.a.hold.push_back( new long_life );
...and then you create a copy of this myz:
z my_other_z;
// ...
my_other_z = myz;
The operator= implementation you have provided above simply copies the contents of the vector. If the vector has pointers, it doesn't make copies of whatever's being pointed to -- it just makes a literal copy of the pointer itself.
So after operator= returns, you will have 2 instances of z that have pointers pointing to the same thing. When the first of those zs is destructed, it will delete the pointer:
~z(){ for( auto it=hold.begin();it!=hold.end() ++it ) delete(*it); };
When it comes time for the second z to be destroyed, it will try to delete the same pointer a second time. This results in Undefined Behavior.
The solution to this problem is to make deep copies when you assign or copy objects that maintain resources that need to be allocated and deleted. That means providing an assignment operator and a copy constructor.
That is why the rule of three is a RULE.
EDIT:
As others have mentioned, this is all better avoided altogether by using value semantics and RAII. Reengineering your objects to use the Rule of Zero, as others have called it, is a much better approach.
Actually there will be a double free here, not a memory leak.
STL containers store objects, not references. In your case object is a pointer. Pointers are simply copied. Your line a2 = a; will duplicate pointer in the vector. After that each destructor will release the pointer.
Double free is much more dangerous than the memory leak. It causes nasty undefined behavior:
MyStruct *p1 = new MyStruct();
delete p1;
.... do something, wait, etc.
delete p1;
at the same time on the other thread:
MyOptherStruct *p2 = new MyOtherStruct();
.... do something, wait, etc.
p2->function();
It may turn out that memory allocator will assign to p2 exactly the same value that was used for p1, because it is free after the first call to delete p1. A while later second delete p1 will also go fine because allocator thinks that this is a legitimate pointer that was given out for p2. The problem will appear only at p2->function();. Looking at the code of thread 2 it is absolutely impossible to understand what went wrong and why. This is extremely difficult to debug, especially if the system is big.