Presentation is loading. Please wait.

Presentation is loading. Please wait.

CS 144 Advanced C++ Programming April 30 Class Meeting

Similar presentations


Presentation on theme: "CS 144 Advanced C++ Programming April 30 Class Meeting"— Presentation transcript:

1 CS 144 Advanced C++ Programming April 30 Class Meeting
Department of Computer Engineering San Jose State University Spring 2019 Instructor: Ron Mak

2 Unofficial Field Trip Computer History Museum in Mt. View
Provide your own transportation to the museum. Saturday, May 4, 11:30 – closing time Special free admission. We will meet in the lobby. No backpacks. Experience a fully restored IBM 1401 mainframe computer from the early 1960s in operation. Do a self-guided tour of the Revolution exhibit.

3 Pointers vs. References
The value of a pointer variable is the address of another variable. Pointer variable p1 contains the address of variable i. Pointer variable p2 contains the address of the dynamically allocated Birthday object. A reference variable is an alias for an already existing variable. Both r1 and i now refer to the same variable. int i, j; int *p1 = &i; Birthday *p2 = new Birthday(1963, 9, 2); int& r1 = i;

4 Pointers vs. References, cont’d
Both pointers and references are implemented as addresses, but there are major differences. A pointer variable can be reassigned: A reference variable must be assigned at initialization and then cannot be reassigned. A pointer variable can be null. A reference variable can only be an alias for another variable. p1 = &j; p2 = new Birthday(1967, 4, 3); int& r1 = i; p2 = nullptr;

5 Pointers vs. References, cont’d
Explicitly dereference a pointer variable. A reference variable is implicitly dereferenced. A pointer variable’s value can participate in address arithmetic. A reference variable’s address value cannot. A reference variable is similar to a constant pointer. int k, y; k = *p; y = p2->year; Birthday bd(1960, 9, 2); Birthday& rbd = bd; rbd.year = 1964;

6 Use of References When you declare a function’s parameter to be a reference, you are saying that the parameter is an alias of the caller’s argument. Example: Function parameter parm is an alias for the caller’s argument k. Therefore, any changes to parm in the function changes k instantaneously, because they are the same variable. void func(int& parm) ... ... func(k);

7 Use of References, cont’d
A function can return a reference. Example: Special variable this is a pointer to the object, and therefore *this is the object itself. We are returning an alias to the object. SafeArray& SafeArray::operator =(const SafeArray& rhs) { ... return *this; }

8 Use of References, cont’d
You can pass a pointer by reference. The syntax is a bit awkward. Example: or: or: Note the order of the * and the &. void func(int* &ptr); void func(int*& ptr); void func(int *&ptr);

9 Use of References, cont’d
The overridden assignment operator should return a reference so that in a chained assignment like would work. Recall the signature of a copy constructor. The assignment operator is right associative. is executed as a = b = c; MyClass(const MyClass& other) a = b = c; a = (b = c);

10 Deep Copy If you want a deep copy, one that includes copying the data, you need to write your own copy constructor. BirthdayDeepCopy/BirthdayDynamic.cpp BirthdayDynamic::BirthdayDynamic(const BirthdayDynamic& other)     : length(other.length), elements(new Birthday[length]) {     // Copy the elements.     for (int i = 0; i < length; i++) elements[i] = other[i]; } Create dynamic birthday array: Print dynamic birthday array: 0/0/0 9/2/1981 5/8/1992 End of program!

11 Problems with a Standard (“Raw”) Pointer
A raw pointer’s declaration doesn’t indicate whether: It “owns” the object that points to (should you delete the object?) It points to a single object or to an array (should you call delete or delete[]?) Should you call a dedicated destructor function? Will you delete the object exactly once? Memory leak if you never delete the object. Segmentation fault if you delete the object more than once.

12 Raw Pointers vs. Smart Pointers
Raw pointers are powerful but dangerous. Use a smart pointer instead. Unique pointer Has exclusive ownership of the object it points to. The ownership can be transferred. The object is automatically deleted when the pointer goes out of scope. Shared pointer Multiple pointers can point to the same object. The object is deleted only once.

13 Unique Pointer Exclusive ownership of the object.
An object can have at most one unique pointer at a time pointing to it. Ownership can be transferred to another unique pointer. The source pointer is automatically set to null. When the owning pointer is set to point to another object, or goes out of scope, the object it points to is automatically deleted.

14 Unique Pointer Example
We’ll use our old Birthday class. The default constructor, constructor, and destructor each writes a message when it’s called. TestUnique.cpp int main() {     cout << "raw_ptr ..." << endl;     Birthday *raw_ptr = new Birthday(2001, 1, 1);     cout << "*raw_ptr   = " << *raw_ptr   << endl;     cout << endl << "uniq_ptr1 and uniq_ptr2 ..." << endl;     unique_ptr<Birthday> uniq_ptr1(new Birthday(2002, 2, 2));     unique_ptr<Birthday> uniq_ptr2(nullptr);     cout << "*uniq_ptr1 = " << *uniq_ptr1 << endl; ... raw_ptr ... *** Constructor called for 1/1/2001 *raw_ptr   = 1/1/2001 uniq_ptr1 and uniq_ptr2 ... *** Constructor called for 2/2/2002 *uniq_ptr1 = 2/2/2002

15 Unique Pointer Example, cont’d
“Move” the object from one pointer to another. Can’t copy, because of exclusive ownership. The source pointer is automatically set to nullptr.     cout << endl << "uniq_ptr2 = move(uniq_ptr1) ..." << endl;     uniq_ptr2 = move(uniq_ptr1);     if (uniq_ptr1 == nullptr) cout << "uniq_ptr1 is null" << endl;     else                      cout << "*uniq_ptr1 = " << *uniq_ptr1 << endl;     cout << "*uniq_ptr2 = " << *uniq_ptr2 << endl; uniq_ptr2 = move(uniq_ptr1) ... uniq_ptr1 is null *uniq_ptr2 = 2/2/2002

16 Unique Pointer Example, cont’d
You can associate a custom destructor with a unique pointer. In this example, we use a lambda expression. Also note the use of decltype.     function<void(Birthday *bptr)> delete_birthday = [] (Birthday *ptr)     {         cout << "Deleting " << *ptr << endl;         delete ptr;     };     cout << endl << "uniq_ptr3 ..." << endl;     unique_ptr<Birthday, decltype(delete_birthday)>         uniq_ptr3(new Birthday(2003, 3, 3), delete_birthday);     cout << "*uniq_ptr3 = " << *uniq_ptr3 << endl; uniq_ptr3 ... *** Constructor called for 3/3/2003 *uniq_ptr3 = 3/3/2003

17 Unique Pointer Example, cont’d
Use the reset member function to change the value of a unique pointer.     cout << endl << "uniq_ptr4 ..." << endl;     unique_ptr<Birthday> uniq_ptr4(nullptr);     uniq_ptr4.reset(new Birthday(2004, 4, 4));     cout << "*uniq_ptr4 = " << *uniq_ptr4 << endl;     cout << endl << "uniq_ptr4.reset ..." << endl;     uniq_ptr4.reset(new Birthday(2005, 5, 5)); uniq_ptr4 ... *** Constructor called for 4/4/2004 *uniq_ptr4 = 4/4/2004 uniq_ptr4.reset ... *** Constructor called for 5/5/2005 *** Destructor called for 4/4/2004 *uniq_ptr4 = 5/5/2005

18 Unique Pointer Example, cont’d
When a unique smart pointer goes out of scope, the object it points to is automatically removed from memory.     cout << endl << "Program termination ..." << endl;     return 0; } Program termination ... *** Destructor called for 5/5/2005 Deleting 3/3/2003 *** Destructor called for 3/3/2003 *** Destructor called for 2/2/2002 Memory leak! Birthday *raw_ptr = new Birthday(2001, 1, 1);

19 Shared Pointer Multiple shared smart pointers can point at the same time to a single object. Shared-ownership resource management. When the last pointer no longer points to the object, the object is automatically deleted. It is deleted only once. Uses reference counts.

20 Shared Pointer Example
int main() {     cout << endl << "shar_ptr1 and shar_ptr2 ..." << endl;     shared_ptr<Birthday> shar_ptr1(new Birthday(2001, 1, 1));     shared_ptr<Birthday> shar_ptr2(shar_ptr1);     cout << "*shar_ptr1 = " << *shar_ptr1 << endl;     cout << "*shar_ptr2 = " << *shar_ptr2 << endl;     {         cout << endl << "Entering new scope!" << endl;         shared_ptr<Birthday> shar_ptr3(shar_ptr2);         shared_ptr<Birthday> shar_ptr4(shar_ptr3);         cout << "*shar_ptr1 = " << *shar_ptr1 << endl;         cout << "*shar_ptr2 = " << *shar_ptr2 << endl;         cout << "*shar_ptr3 = " << *shar_ptr3 << endl;         cout << "*shar_ptr4 = " << *shar_ptr4 << endl;         cout << "Exiting scope!" << endl << endl;     }     cout << endl << "Program termination ..." << endl;     return 0; } TestShared.cpp Nested scope

21 Shared Pointer Example, cont’d
int main() {     cout << "shar_ptr1 and shar_ptr2 ..." << endl;     shared_ptr<Birthday> shar_ptr1(new Birthday(2001, 1, 1));     shared_ptr<Birthday> shar_ptr2(shar_ptr1);     cout << "*shar_ptr1 = " << *shar_ptr1 << endl;     cout << "*shar_ptr2 = " << *shar_ptr2 << endl; shar_ptr1 and shar_ptr2 ... *** Constructor called for 1/1/2001 *shar_ptr1 = 1/1/2001 *shar_ptr2 = 1/1/2001

22 Shared Pointer Example, cont’d
    {         cout << endl << "Entering new scope!" << endl;         shared_ptr<Birthday> shar_ptr3(shar_ptr2);         shared_ptr<Birthday> shar_ptr4(shar_ptr3);         cout << "*shar_ptr1 = " << *shar_ptr1 << endl;         cout << "*shar_ptr2 = " << *shar_ptr2 << endl;         cout << "*shar_ptr3 = " << *shar_ptr3 << endl;         cout << "*shar_ptr4 = " << *shar_ptr4 << endl;         cout << "Exiting scope!" << endl << endl;     } Entering new scope! *shar_ptr1 = 1/1/2001 *shar_ptr2 = 1/1/2001 *shar_ptr3 = 1/1/2001 *shar_ptr4 = 1/1/2001 Exiting scope! Nothing special happens when we exit the scope. shar_ptr3 and shar_ptr4 go out of scope.

23 Shared Pointer Example, cont’d
    cout << "*shar_ptr2 = " << *shar_ptr2 << endl;     cout << endl << "Program termination ..." << endl;     return 0; *shar_ptr2 = 1/1/2001 Program termination ... *** Destructor called for 1/1/2001 The destructor is automatically called once the last pointer to the object goes out of scope.

24 Shared Pointer Implementation
Effective Modern C++ by Scott Meyers O’Reilly, 2015 ISBN

25 Introduction to Move Semantics
As we’ve seen, excessive calls to the copy constructor can hurt performance. In a move from source to target, instead of copying a resource, the target “steals” the resource from the source. The source contains a pointer to an object. The target gets a copy of the pointer. The source’s pointer is set to null. Therefore, the target has taken ownership of the object.

26 Introduction to Move Semantics, cont’d
Move semantics involve lvalues and rvalues. An lvalue is an object. So called because it can appear on the left side of an assignment statement (i.e., it can be an assignment target). An rvalue is a temporary value or a value not associated with an object. So called because it can be a value that’s calculated on the right side of an assignment statement.

27 Introduction to Move Semantics, cont’d
When an rvalue is assigned in an assignment statement, or when an rvalue is passed by value to a function, a copy is made of the value. But it is wasteful to copy a temporary value. That value normally is about to disappear. Move semantics allows us to use that temporary value by taking ownership of its resources. Reduce the amount of runtime copying.

28 Move Semantics Example
Message.h class Message { private:     int length;     char *text;      public:     Message() : length(0), text(nullptr) {}     Message(char *t) : length(strlen(t)), text(t) {}     virtual ~Message()     {         if (length > 0) delete[] text;         length = 0;         text = nullptr;     }

29 Move Semantics Example, cont’d
    Message(const Message& other)     {         cout << "*** Copy constructor called!" << endl;         length = other.length;         text = new char[length + 1];         strcpy(text, other.text);     }     Message(Message&& other)         cout << "*** Move constructor called!" << endl;         text = other.text;         other.length = 0;         other.text = nullptr; Copy constructor Copy the source’s data. Move constructor Steal the source’s data without copying it.

30 Move Semantics Example, cont’d
Message& operator =(const Message& other) {     cout << "*** Copy assignment called!" << endl;     if (this != &other)     {         if (length > 0) delete[] text;         length = other.length;         text = new char[length + 1];         strcpy(text, other.text);     }     return *this; } Copy assignment Message& operator =(Message&& other) {     cout << "*** Move assignment called!" << endl;     if (this != &other)     {         if (length > 0) delete[] text;         length = other.length;         text = other.text;                      other.length = 0;         other.text = nullptr;     }     return *this; } Move assignment Steal the source’s data without copying it.

31 Move Semantics Example, cont’d
friend ostream& operator <<(ostream& outs, const Message& msg) {     cout << msg.length << ":";     if (msg.text != nullptr) cout << msg.text;     else                     cout << "<empty>";     return outs; }

32 Move Semantics Example, cont’d
TestMove.cpp char h[16], w[16]; strcpy(h, "hello"); strcpy(w, "world"); cout << "h = " << h << endl; cout << "w = " << w << endl; cout << endl << "Default constructor:" << endl; Message msg; cout << "msg = " << msg << endl; cout << endl << "Regular constructor:" << endl; Message hello1(h); Message world1(w); cout << "hello1 = " << hello1 << endl; cout << "world1 = " << world1 << endl; h = hello w = world Default constructor: msg = 0:<empty> Regular constructor: hello1 = 5:hello world1 = 5:world

33 Move Semantics Example, cont’d
cout << endl << "Copy constructor:" << endl; Message hello2(hello1); cout << "hello1 = " << hello1 << endl; cout << "hello2 = " << hello2 << endl; cout << endl << "Overloaded copy assignment operator:" << endl; Message world2; world2 = world1; cout << "world1 = " << world1 << endl; cout << "world2 = " << world2 << endl; Copy constructor: *** Copy constructor called! hello1 = 5:hello hello2 = 5:hello Overloaded copy assignment operator: *** Copy assignment called! world1 = 5:world world2 = 5:world

34 Move Semantics Example, cont’d
cout << endl << "Move constructor:" << endl; Message hello3(move(hello1)); cout << "hello1 = " << hello1 << endl; cout << "hello3 = " << hello3 << endl; cout << endl << "Overloaded move assignment operator:" << endl; Message world3; world3 = move(world1); cout << "world1 = " << world1 << endl; cout << "world3 = " << world3 << endl; Move constructor: *** Move constructor called! hello1 = 0:<empty> hello3 = 5:hello Overloaded move assignment operator: *** Move assignment called! world1 = 0:<empty> world3 = 5:world

35 Review: Rule of the “Big Three”
If a class defines one or more of the following, it should explicitly define all three: Destructor Copy constructor Copy assignment operator The copy constructor and the copy assignment operator each does a deep copy.

36 Rule of the “Big Five” Explicitly define:
Destructor Copy constructor Copy assignment operator Move constructor Move assignment operator The move constructor and the move assignment operator can each steal resources from temporary source objects.


Download ppt "CS 144 Advanced C++ Programming April 30 Class Meeting"

Similar presentations


Ads by Google