Classes part 3 - Copy Constructor, Copy Assignment(Deep and Shallow Copy) | Modern Cpp Series Ep. 39
HTML-код
- Опубликовано: 10 сен 2024
- ►Full C++ Series Playlist: • The C++ Programming La...
►Find full courses on: courses.mshah.io/
►Join as Member to Support the channel: / @mikeshah
►Lesson Description: In this lesson I cover the copy constructor, and copy assignment operator. I start by showing how the compiler gives you these two special functions for classes for free, using the compiler generated ones. Unfortunately, this is not sufficient, as when we have to dynamically allocate memory, the trivial copy can result in problems like double frees or unwanted sharing of data. Next I show you how to avoid making these 'shallow' copies, and write your own copy constructor and copy-assignment operators to perform a deep copy.
►RUclips Channel: / mikeshah
►Please like and subscribe to help the channel!
Does "Array myArray2 = new Array(myArray); " triggers copy-constructor as well?? If yes, I assume the difference is allocating myArray2's memory on heap?
I might need to see a working example of this -- meaning:
Array on the left-hand side needs to be a pointer. You can certainly override the new operator and pass in a type.
However, with a normal copy constructor (e.g. Array myArray2 = myArray) -- the copy constructor would take care of the deep copy, and likely allocate on the heap any memory needed.
There's something called 'placement new' otherwise (slightly different syntax) that otherwise can allocate in already allocated heap memory.
@@MikeShah For example:
template
struct eigen_base {
T* components_;
eigen_base() {
components_ = nullptr;
}
eigen_base(const eigen_base& other) {
this->components_ = other.components_ ? new T(*other.components_) : nullptr;
}
eigen_base(const T& other) {
this->components_ = new T(other);
}
eigen_base& operator=(const eigen_base& other) {
if (this != &other) {
this->components_ = other.components_ ? new T(*other.components_) : nullptr;
}
return *this;
}
......
};
What does "new T(xxxx)" trigger?
@@kenk4552I think I understand the question now, so I wrote an example as well. It calls the copy constructor and heap allocates the object.
godbolt.org/z/6oGMe96Tv
#include
struct Type{
Type(){
std::cout
@@MikeShah Great great example !!!! Thank you so much for putting in time to respond to every single one of our questions !! Huge respect!
Wow, I dont understand how this is not 100k+ views, this is by far the best explanation I"ve seen of this concept !
Cheers, thank you for the kind words!
Thanks for publishing these videos to public. Your teaching style is fantastic.
Thank you for the kind words!
This is the best c++ playlist on youtube by far!!!!
Thank you for the kind words 🙏
Fantastic tutorial, as I'm learning UE5 C++, this tutorial is helping me a lot to understand what is happening there
Cheers!
You are a great teacher thank you so much for this valuable content
please continue
Cheers, you are most welcome!
Omgg 😮 this is c++ goldmine so excited to complete this playlist.
Request: please make videos on building tools using c++ , I always want to develop something using c++ but dunno how to start, thanks man.
Cheers! This may be a good starting point for a small project -- courses.mshah.io/courses/quick-start-introduction-to-modern-c-image-loader
So thankful for all your amazing lessons
Cheers, you are most welcome! Happy to have you as part of the community!
Interesting channel. C++ topics are very well explained here.
Cheers -- welcome aboard!
Damn ! Such a clear and wonderful explanation.! You deserve much more views and subs sir...!
It would be very useful if you could do a video on namespace , scope of variablesand functions in namespace, inline keyword and how scopes and lifetimes of identifiers defined in different circumstances and the best practices to be used in header files!
And im a bit confused between difference between file scope vs global scope and gobal namespace parent namespaces. It'd be of great help if you could enlighten me ! Thanks a lot!!
Cheers! I have a few videos on scope, inline, and more in the playlist 🙂
@@MikeShah sure I'll check them out and thank you so much for these!😊
Thanks for making this video. It's an excellent explanation.
Cheers, thank you for the kind words!
Thank you , you are the best
Cheers!
Mike, a quick question. In the copy assignment function why do we delete[] before assigning new heap area for the new data object? delete[] operation marks the area as free right, thereby making the data pointed by the original object a dangling pointer? Am I missing something here? Please help! Thanks
We delete[] first because during assignment we are potentially copying to an array of a different size (not in this example since the array is always 10 integers, but this is just an example). If we don't free the memory, before allocating again, then we are left with a memory leak.
Great explanation Mike Thanks.
Cheers!
Very neatly explained as always. Thank You
Cheers!
Just wanted to stop by and see you're awesome! I just started learning C++ coming from python and Go and your videos have really helped me. They are currently my main source of learning! Thanks for the great content.
Cheers, thank you for the kind words!
My go-to resource for C++ tutorials, you're the best! 📚🙌
Cheers! Thank you for the kind words 🙏
Hi Mike, Thank you for the video. This is the best video that i watched which explains both copy constructor and copy assignment operator clearly.
Cheers!
Thanks for the clear explanation
But do we need to delete the allocated memory and reallocate it again in the copy assignment operator?
Need to remove the previously allocated memory, for which we are going to copy over. This step is not always strictly necessary in special cases where we for example have a dynamically allocated array of the same size -- we could just copy elements over that allocated memory. That said, in our copy constructor, we need to make sure that we perform a 'deep copy' to own our memory in our object, and it's generally a good idea to remove any previously allocated resources.
Thanks, Prof
Can’t get enough
MasterClass
Kept revisiting
Quite clear in my head
Cheers, you're most welcome Joe!
Hey Mike! Thanks for the videos can you explain why we need to return as Array& , we can simply use Array right?
We return Array& in the copy-assignment operator (i.e. operator= function). If you look carefully at the return value, you'll notice it as '*this'. 'this' is the internal pointer to the current instance of the object. We derference that object, and return the actual object, that is, not the actual pointer. Because we are doing an actual assignment of an object, we need to return a reference type, that is, Array&.
@@MikeShah Thanks for the explanation!!
Thinking back to previous video, what if the pointer were a unique pointer? Before you made the copy constructor?
Copies of unique pointers as you mentioned are forbidden. You'll find something here in cppreference on it not being copyassignable en.cppreference.com/w/cpp/memory/unique_ptr. So whatever your type is, you can do move construction or move assignment with unique pointers, but no copies allowed because by definition that means you'd have two unique_ptrs sharing the same data. So that was a long winded way of me just saying, you'll do the 'deep copy' yourself with a unique_ptr just like you would with a normal pointer :)
Hi Mike, At 12.25 , referring to the class Array. Question is When in main function control will reach at myArray.printingData(); how and why this pointer will point to the data created on the heap using the data = new int[10];
As soon as the array is constructed at line 23, the constructor executes and allocates new int[10]. Each instance of 'Array' (i.e. myArray in this example) has its own member variable associated with it (in this case there is one member variable data in the class). Let me know if that's helpful. If you want to see how this is implemented, I have a C lesson giving an idea on how Object-Oriented programming is implemented: ruclips.net/video/cR2QFQbssa0/видео.html
Hey Mike! Thanks for the awesome videos!
At 24:55 line 20, can you explain why we can directly access rhs's (the argument we passed in the copy constructor) data member (int* data) which is declared as private in the class declaration? I thought we need to use something like getter in copy constructor just like we can't directly set it so we need SetData function and use it in the main function.
Thanks in advance!
Since we have passed in the 'rhs' by reference, it is the actual values. Pass by reference avoids making a copy, so we access the actual data. Since this is of the same type (i.e. rhs is also of type 'Array'), we can access the private variables :)
Very nice explainations again. Thanks
Just one question. Inside copy assignment operator, we have first if statement and it checks if &rhs==this; return *this. So here rhs is from first object and “this” is from the second object of the class"?. How can we know where is “ this “ beforehand. Because we are deleting data after if statement and creating new data on the heap then return by dereferencing “this”.
I hope my question makes a sense.
But still, If my question is not clear, that would be great if you could explain one more time first if statement inside copy assignment operator. Why do we return *this if the staement is true?
We need to return some value -- effectively it's saying 'don't go through the effort of doing a potentially expensive copy if we are in fact attempting to copy ourselves'.
Thanks for the informative video once again! Was wondering when discussing the order of free operations when array1 and array2 go out of scope, would it actually be the case that array2 is deleted first due to the nature of the stack, meaning it deletes array1s data before array1 even gets the chance?
Do you have a timestamp? (I'm assuming myArray and myArray2 within the main() for my response) -- the answer is that objects are destroyed in a Last In, First Out (LIFO) style, so myArray2 indeed is destroyed first. This is deterministic in C++, and it will always follow this order :)
@@MikeShah Yes that is the example. Thank you so much for the clarification and once again thanks for the playlist! Coming from C it's been really rewarding watching these videos.
@@moderig cheers!
thank you:)
Cheers!
Dr. Shah, can I ask about ur opinion to learn CUDA C++ for computer graphics and computer vision as a final year bachelor student just about to start a career ?
Or this is lower priority to learn than other things ?
and really THANK YOU Dr. Shah
I think CUDA would be a wonderful skill to learn in your case. It teaches another way to think about computation 🙂
Hi Mike, how would you go about declaring the copy assignment operator in an interface (.hpp) and its corresponding implementation (.cpp)? I'm following the tutorial using this format and I seem to have hit a syntactical roadblock.
Copy Assignment operator in the .cpp would be something like: Array& Array::operator=(const Array& rhs){ ... }
@@MikeShah got it. I also realized you already had the answer in the next video in the series. Thanks a mill.
@@ThislsYusuf cheers!
Hello Dr. Shah, awesome lecture!! Just a question, why do we need to delete [] data in the copy constructor? Since the object is not initialized yet why would data be pointing to something?
Got it, clarified later in the video
@@MrSals123 Cheers!
Hi Mike,
I have a question about copy constructor. As i understood when you created second object copy constructor was called. No data was allocated to second object before. Is it valuable to write delete[] data; inside copy constructor (after that you create dynamic array)?? I Think that this delete[] gives us nothing, but maybe im wrong 😊 Thanks!
For operator= the object has already been initialized once, so it is important to delete.The 'copy constructor' (named same as the function) otherwise is initializing the object for the first time, so no need to delete anything.
Why to delete data in assignment operator when it is having a allocated memory. Can't we just copy new elements from source object to data memory directly
If the allocation is the same size, we certainly would not have to delete it -- so for this video it's true we always have 10 ints. Keep in mind, that is for demonstration purposes, we'll have to delete old data and then reallocate a new buffer to put memory in if size is different (of course if new allocation is smaller, you could also track capcity/length and make other decisions as needed.)
@@MikeShah ok, thanks for your reply. It was a great video.
Cheers! @@TechTricks1992
Hi sir, at 25:14, how do we say that myArray2 specifically called the copy constructor ?
myArray is being 'created by copy' -- thus the copy constructor is invoked as the new object 'myArray2' is being created for the first time. This is not to be confused with the 'assignment operator (i.e. operator=)' -- we are infact invoking the copy constructor at line 41 at 25:14
Hi, I just found out that a = b; had called operator = instead of copy constructor. Am I correct?
Correct! Becase a is already constructed -- i.e. you construct a new type when the type declaration is to the left of the type (e.g. 'Student a;' calls the constructor, 'Student a = b' 'would call the 'copy constructor' to make a as a copy of b during construction)
@@MikeShah Thank you. This explanation is precious ❤️
Cheers! @@peerajak
Thank you Mike! Excellent video! Learnt a lot. I just had one small doubt. For the Copy Assignment Operator Overload do we really need a return type of Array reference object? Since the copy assignment operator overload function is a member function it has access to the private member pointer *data. So it can very well copy the values from rhs object into the this objects data pointer. So we can code the function like so as well: void operator=(const Array& other) {
std::cout
Indeed, the return type in the provided example does not make a difference to the copy taking place. I believe however, if you want to chain together expressions, you need a return value.
e.g.
A = B = C // having a 'void' in here will break the type system.
(A = B).callFunc(); // Though I probably would not recommend writing code this way anyway.
So perhaps if you want the ultimate flexibility in your code (so you can chain) return the type, otherwise if you want to disallow chaining, use 'void' -- which in some way, actually indeed seems preferable :)
Thank you for taking the time out to reply to my comment! Means a lot and please do continue making the awesome videos you make! God bless you 🙏
Cheers! @@dwivedys