Excellent example Mike. Neat, plain, simple, fast and complete. I would really appreciate if you can post a similar video showing all the possible ways to build a vector (or any other STL container for that matter) of objects in C++ highlighting the best practice to do so. You can create vectors of objects from the stack or from the heap, you can use move semantics, you can use objects themselves or pointers to them, etc … Many possibilities here, but what is the best way of doing things ? Thanks for these very good and informative videos!
Thank you for the kind words! Coming up in the series will eventually be talking about STL and these general data structures available. Then I've noted your request, as at some point this is the kind of video that could go into a Data Structures Playlist for C++.
Yeah, that's indeed part of it. If we think in lifetimes of the objects, the first thing created is put on the bottom of the stack. The second thing's destructor would then be pushed on top (last-in, first-out or LIFO). It then makes sense that we'd want to destroy the 'second thing' prior to the 'first thing', because it's also possible the second thing could have referenced/depend on the first thing.
Hi Mike, I am wondering what plugin you use for getting all the variables you created to pop up as you type? I like that it helps organize the variables you have created without giving you every keyword or function available in c++. Currently, I am using nvim with no LSP support.
Just using regular VIM, no plugins (Ctrl+p). See th-cam.com/users/results?search_query=mike+shah+vim or courses.mshah.io/courses/vim for more. I do also show how to use 'bear' in one of my TH-cam videos otherwise show how to do more code completion stuff if you want an 'LSP' type experience.
Hi @MikeShah at 8:30 , can you please explain how you returned result from foo(), as the function foo() has return type of IntArray, BUT at it's call from main(), you have not written any variable to store it, isn't it an error or warning?
In the move assignment operator, it makes sense why m_data from the source would be set to nullptr to avoid a double delete error in the destruction of the object. Running through valgrind it shows more allocations than frees when calling a move assignment. But when I switch to using std::move on m_data, valgrind still shows that there is unfreed memory - but now has equal allocations to frees. Any insights into what is happening? Also, would this be considered leaking data, or just the natural consequence of allocating memory just to reassign it in this example?
This cleared up a confusion I had about the move operation. It didn't make sense to me, why would we reset variables of the source if the destination was claiming its memory, it seemed as if resetting source was resetting destination. But then I see, it's only for heap allocated memory that move operations truly moves, whereas stack variables are copied. Thanks for the video.
@@MikeShah Yeah just wanted to put that comment in there so anyone who watches this vid knows to do so. I am loving the series and learning some great things, thank you for making these
Hello Mike, I am very thankful for your videos; I learn a lot from you. In this video, regarding the implementation of the move constructor, why didn’t you choose to change the ownership of the source object’s data members by using std::move, and instead did it manually?
Hi Mike, why you just assigned data directly in move constructor instead of creating a new memory and copying content inside it. To me it seems we are doing shallow copy here. And due to that only valgrind reported less memory allocations.
Since the move constructor transfers ownership and the original allocated object is being nullified, wouldn't it be even more efficient, in terms of size of allocated memory, to delete the original afterwards?
One thing to keep in mind is that 'moving' is not equivalent to 'destroying' an object. The nullified object will otherwise be reclaimed by the destructor and save memory in that way. I suppose if there was some way to know exactly how many objects you needed, you would save stack space when allocating those objects to otherwise not allocate them, and just keep the object you need that owns the memory you'll need. Pool allocators for example are designed to optimize for allocatations where you may have a fixed size number of objects, and otherwise can avoid major reallocations by just transferring and 'recyling' (i.e. moving) memory around.
Hey Mike, amazing video! You really helped me understand the rule of 5 better. I am just wondering why you didn't delete the array before giving m_data the new value. Doesn't that lead to a memory leak ?
@@karm0s304 When we 'move assign', we are transferring ownership. So I don't want to destroy the data, but rather only have one pointer to the data. If I delete the data, then the data will be null :) Remember the '=' with a pointer type is simply updating the pointer to the memory. Note also, for the 'std::string' m_name, I am using an '=' sign to transfer ownership, but the move-assignment operator that has implemented in std::string should do the proper 'move-assignment' for us (as opposed to a copy).
@@MikeShah Thanks for your reply ! Actually I am talking about the old data that m_data was holding before giving it the new pointer. If I understood correctly, the move assignment operator method will be called when we try to move an object into another existing object. Let's say I try to move B into A, object A's m_data will have a pointer to an array of 10 elements, so if we move the value of B's m_Data into A's m_Data without deleting the old array that A was holding that would cause a memory leak. The only place where we could directly do that is in the move constructor because the receiving object didn't allocate any data before.
very excellent lecture. Thanks Mike. In the move assignment operator implementation, should we be freeing the allocated memory by calling `delete[] m_data` before assigning m_data to source.m_data? In the copy assignment operator, we freed the memory using delete[] m_data. Shouldn't we follow the same practice in the move assignment operator?
Should not need to free the data, we are just re-assigning the pointer -- move assignment thus is faster, as it's taking over ownership of the data without having to reallocate. Old m_data then points to nothing (i.e. no 'sharing' or 'moving' of data -- it is effectively empty or nullptr)
@@MikeShah, if move assignment is invoked, valgrind will show memory leaks. Before repointing, the existing memory needs to freed for assignment. Your example only used move constructor. Also, better to use std::move on the string member m_name and no need to assign it to "" afterwards either.
Correct, do need to eventually free the data, just not after 'moving' it (so still need to do so in the destructor of whoever owns the memory)@@mingxue6327
@@MikeShah We should, right? Because in the move assignment, we are first creating a new object and then assigning it to another. So, we do need to do the following: delete[] m_data; m_data = nullptr;
I'm very new to C++ mostly with Java knowledge, I wonder why would one chose to define unimplemented constructors, I understand the point of interfaces but what's the need for a constructor with no implementation?
A unique_ptr itself can be moved (not copied however). So you could do something like: std::unique_ptr object1 = std::move(object2); in your move-assignment constructor. This might provide some insight: docs.microsoft.com/en-us/cpp/cpp/how-to-create-and-use-unique-ptr-instances?view=msvc-170
Hi @MikeShah, why you just assigned data directly in move constructor instead of creating a new memory and copying content inside it. To me it seems we are doing shallow copy here. And due to that only valgrind reported less memory allocations.
Move assignment or move constructor is in fact doing a shallow copy -- it's fast! But along with the shallow copy, we are changing ownership, such that the new object has control of the data, and the other object we are moving from, then does not 'own' the memory.
@@MikeShah 17:01 I meant why is it doing (copies) while reallocating memory but then i understood my confuse. We "move" ownership not the data, so when the vector allocates new memory to hold more space it needs to COPY the data from old memory. Sorry, it was stupid question. Thank you for your response and for your lessons!
Excellent example Mike. Neat, plain, simple, fast and complete. I would really appreciate if you can post a similar video showing all the possible ways to build a vector (or any other STL container for that matter) of objects in C++ highlighting the best practice to do so. You can create vectors of objects from the stack or from the heap, you can use move semantics, you can use objects themselves or pointers to them, etc … Many possibilities here, but what is the best way of doing things ? Thanks for these very good and informative videos!
Thank you for the kind words! Coming up in the series will eventually be talking about STL and these general data structures available. Then I've noted your request, as at some point this is the kind of video that could go into a Data Structures Playlist for C++.
Is the reason for the reverse destruction at 6:25 due to the nature of a program's stack?
Yeah, that's indeed part of it. If we think in lifetimes of the objects, the first thing created is put on the bottom of the stack. The second thing's destructor would then be pushed on top (last-in, first-out or LIFO). It then makes sense that we'd want to destroy the 'second thing' prior to the 'first thing', because it's also possible the second thing could have referenced/depend on the first thing.
Hi Mike, I am wondering what plugin you use for getting all the variables you created to pop up as you type? I like that it helps organize the variables you have created without giving you every keyword or function available in c++. Currently, I am using nvim with no LSP support.
Just using regular VIM, no plugins (Ctrl+p). See th-cam.com/users/results?search_query=mike+shah+vim or courses.mshah.io/courses/vim for more. I do also show how to use 'bear' in one of my TH-cam videos otherwise show how to do more code completion stuff if you want an 'LSP' type experience.
Hi @MikeShah at 8:30 , can you please explain how you returned result from foo(), as the function foo() has return type of IntArray, BUT at it's call from main(), you have not written any variable to store it, isn't it an error or warning?
If I mark the function as nodiscard, then a warning will be given since the returned value is not used.
@@MikeShah Thank you so much 😁
Wonderful series
Been on a binge watch since yesterday
Cheers! Thanks Nishant!
In the move assignment operator, it makes sense why m_data from the source would be set to nullptr to avoid a double delete error in the destruction of the object. Running through valgrind it shows more allocations than frees when calling a move assignment. But when I switch to using std::move on m_data, valgrind still shows that there is unfreed memory - but now has equal allocations to frees. Any insights into what is happening? Also, would this be considered leaking data, or just the natural consequence of allocating memory just to reassign it in this example?
Interesting -- I wonder if that is a bug? As long as it's showing that all memory is freed that should be okay however (i.e. no leaks detected).
This cleared up a confusion I had about the move operation. It didn't make sense to me, why would we reset variables of the source if the destination was claiming its memory, it seemed as if resetting source was resetting destination. But then I see, it's only for heap allocated memory that move operations truly moves, whereas stack variables are copied. Thanks for the video.
Cheers!
You should also declare the move constructors noexcept so that when using the struct in STL containers it will not default to copying
Correct! I haven't covered noexcept yet in this series. It's probably the right default in C++ to be honest
@@MikeShah Yeah just wanted to put that comment in there so anyone who watches this vid knows to do so. I am loving the series and learning some great things, thank you for making these
@@rdd-technical6824 Cheers and absolutely -- I'm sure that's going to help future folks out :)
Hello Mike,
I am very thankful for your videos; I learn a lot from you. In this video, regarding the implementation of the move constructor, why didn’t you choose to change the ownership of the source object’s data members by using std::move, and instead did it manually?
I think you're right -- would've been a good idea to demonstrate using std::move to ensure move assignment was called for source.m_name.
Hi Mike, why you just assigned data directly in move constructor instead of creating a new memory and copying content inside it. To me it seems we are doing shallow copy here. And due to that only valgrind reported less memory allocations.
@MikeShah I have the same doubt. Please comment on it
Since the move constructor transfers ownership and the original allocated object is being nullified, wouldn't it be even more efficient, in terms of size of allocated memory, to delete the original afterwards?
One thing to keep in mind is that 'moving' is not equivalent to 'destroying' an object. The nullified object will otherwise be reclaimed by the destructor and save memory in that way. I suppose if there was some way to know exactly how many objects you needed, you would save stack space when allocating those objects to otherwise not allocate them, and just keep the object you need that owns the memory you'll need. Pool allocators for example are designed to optimize for allocatations where you may have a fixed size number of objects, and otherwise can avoid major reallocations by just transferring and 'recyling' (i.e. moving) memory around.
Wonderfully explained as always. Thank You.
Cheers, thank you for the kind words!
Hey Mike, amazing video! You really helped me understand the rule of 5 better. I am just wondering why you didn't delete the array before giving m_data the new value. Doesn't that lead to a memory leak ?
Cheers! At what timestamp are you referring?
@@MikeShah At 14:42, line 51 in the move assignment operator method.
@@karm0s304 When we 'move assign', we are transferring ownership. So I don't want to destroy the data, but rather only have one pointer to the data. If I delete the data, then the data will be null :) Remember the '=' with a pointer type is simply updating the pointer to the memory. Note also, for the 'std::string' m_name, I am using an '=' sign to transfer ownership, but the move-assignment operator that has implemented in std::string should do the proper 'move-assignment' for us (as opposed to a copy).
@@MikeShah Thanks for your reply ! Actually I am talking about the old data that m_data was holding before giving it the new pointer. If I understood correctly, the move assignment operator method will be called when we try to move an object into another existing object. Let's say I try to move B into A, object A's m_data will have a pointer to an array of 10 elements, so if we move the value of B's m_Data into A's m_Data without deleting the old array that A was holding that would cause a memory leak.
The only place where we could directly do that is in the move constructor because the receiving object didn't allocate any data before.
Ah, yes you're correct. For the move-assignment operator we should delete[] beforehand (because we are the owner in this example)
Excellent example!!!! 46 lessons have been completed, 87 lessons left. *FIGHTING
Cheers! Well done!
very excellent lecture. Thanks Mike.
In the move assignment operator implementation, should we be freeing the allocated memory by calling `delete[] m_data` before assigning m_data to source.m_data?
In the copy assignment operator, we freed the memory using delete[] m_data. Shouldn't we follow the same practice in the move assignment operator?
Should not need to free the data, we are just re-assigning the pointer -- move assignment thus is faster, as it's taking over ownership of the data without having to reallocate. Old m_data then points to nothing (i.e. no 'sharing' or 'moving' of data -- it is effectively empty or nullptr)
@@MikeShah, if move assignment is invoked, valgrind will show memory leaks. Before repointing, the existing memory needs to freed for assignment. Your example only used move constructor. Also, better to use std::move on the string member m_name and no need to assign it to "" afterwards either.
Correct, do need to eventually free the data, just not after 'moving' it (so still need to do so in the destructor of whoever owns the memory)@@mingxue6327
@@MikeShah We should, right? Because in the move assignment, we are first creating a new object and then assigning it to another. So, we do need to do the following:
delete[] m_data;
m_data = nullptr;
@@sidddddddddddddd No memory leaks on my end :) We want the new object to take over the data in move assignment, so no need to delete.
very useful, thanks Mike
Cheers!
After watching this video, I really should familiarize myself with the std::move!!
I'm very new to C++ mostly with Java knowledge, I wonder why would one chose to define unimplemented constructors, I understand the point of interfaces but what's the need for a constructor with no implementation?
what is that operator= used for ?
I did not see it get called .
Used for assignment for already created objects to copy
Nice video. I'm a bit confused as to how you would move a unique_ptr?
A unique_ptr itself can be moved (not copied however). So you could do something like: std::unique_ptr object1 = std::move(object2); in your move-assignment constructor. This might provide some insight: docs.microsoft.com/en-us/cpp/cpp/how-to-create-and-use-unique-ptr-instances?view=msvc-170
Hi @MikeShah, why you just assigned data directly in move constructor instead of creating a new memory and copying content inside it. To me it seems we are doing shallow copy here. And due to that only valgrind reported less memory allocations.
Move assignment or move constructor is in fact doing a shallow copy -- it's fast! But along with the shallow copy, we are changing ownership, such that the new object has control of the data, and the other object we are moving from, then does not 'own' the memory.
@@MikeShah thanks for your reply Mike. I have one more question can we move construct an object from itself?
Hmm, there is a move constructor, but not sure if it could move from itself while being constructed -- do you have a use case?
@@MikeShah Hi Mike thanks for your reply, I don't have a use case at present.
Could you please make a video on how to call a WebAPI in C++
Will add to the wishlist!
But why std::vector doesn't use the move constructor when it reallocates the memory?
Do you have a timestamp? Can use std::move(vect) to convert to an rvalue if you wat the value moved.
@@MikeShah 17:01 I meant why is it doing (copies) while reallocating memory but then i understood my confuse. We "move" ownership not the data, so when the vector allocates new memory to hold more space it needs to COPY the data from old memory. Sorry, it was stupid question. Thank you for your response and for your lessons!
@@andk6893 Cheers, no worries! You are most welcome!
Great explanation!
Cheers!