Special Member Functions CS 247 Module 4 Scott
Special Member Functions CS 247 Module 4 Scott Chen
Assigned Readings Ø Eckel. Vol. 1 • Chapter 11 – References and the Copy Constructor • Chapter 12 – Operator overloading ( operator= ) • This will help you understand the key concepts behind special member functions
Agenda for Module 4 Ø Default Constructor and Destructor Ø Copy Constructor and Assignment Operator • Copy Swap Idiom Ø Move Constructor and Assignment Ø Equality Operator Ø Special Member Function Invocation
Section 1 Default Constructor and Destructor
Special Member Functions Ø C++ member functions that the compiler provides default versions if not defined by the programmer • • • Default constructor (generated iff no constructor is defined) Destructor Copy constructor Assignment (operator=) Move constructor Move assignment • We will show when these member functions are normally called.
Default Constructor Ø The compiler-generated version of the default constructor has the following member-wise initialization characteristics • • Simple data members (primitive types) Pointer members Member objects Inherited members Base comp_ ptr_ simple_ Uninitialized Initialized using member default constructor Initialized using base class default constructor
Default Destructor Ø The compiler-generated version of the default constructor has the following member-wise destruction characteristics • • Simple data members (primitive types) Pointer members Member objects Inherited members Deallocated Pointer deallocated (not deleted) Cleaned up using member destructor Cleaned up using base class destructor Base Left in Heap Memory leak? comp_ ptr_ simple_ Referenced Data BDP: Overload default destructor to delete pointer members whenever pointer members are present.
Section 2 Copy Constructor and Assignment Operator
Copy Constructor Ø A copy constructor construct a new object whose value is equal to an existing object Ø The following C++ statement would normally invoke the copy constructor to copy ADT instances class Money; void foo(Money m); int main(){ Money m; Money n{m}; Money p = m; foo(p); } //An ADT //A function accepting ADT by Pb. V //Calls constructor //Calls copy constructor //Also calls copy constructor //Calls copy constructor to make a copy on to the stack for Pb. V
Copy Constructor Ø A copy constructor construct a new object whose value is equal to an existing object Ø How to overload a copy constructor in an ADT class Money{ public: Money(); ~Money(); //An ADT //constructor //destructor Money(const Money &m){ amount_ = m. amount_; } private: int amount_; } //copy constructor //copy the value over
Default Copy Constructor Ø The compiler-generated version of the default copy constructor has the following member-wise copy characteristics • • Simple data members (primitive types) Pointer members Member objects Inherited members Bitwise copy Copy using member copy constructor Copy using base class copy constructor Original Base comp_ Copy Base Shallow comp_ ptr_ simple_ Copy Referenced Data
Shallow vs. Deep Copy Ø Shallow Copy • Copy pointer addresses: the pointer members of the original and the copy refer to the same object Original Copy Base comp_ ptr_ simple_ Referenced Data Ø Deep Copy • Copy the referenced data: the pointer members of the original and the copy refer to distinct objects Original Copy Base comp_ ptr_ simple_ Referenced Data
Assignment Operator Ø A. k. a. Copy Assignment Operator • Similar to copy constructor, except the destination of the copy already exists • The following C++ statement would normally invoke the copy assignment operator class Money; int main(){ Money m, p; p = m; } //An ADT //Calls constructor //Calls copy assignment operator
Assignment Operator Ø Similar to copy constructor, except the destination of the copy already exists Ø How to overload a copy assignment operator in an ADT class Money{ public: Money(); ~Money(); //An ADT //constructor //destructor Money& operator= (const Money &m){ this -> amount_ = m. amount_; return *this; } private: int amount_; } //copy assignment //copy the value over //return the copied ADT
Assignment Operator Ø The compiler-generated version of the default copy assignment operator has the following member-wise copy characteristics • • Simple data members (primitive types) Pointer members Member objects Inherited members Bitwise copy Copy using member copy constructor Copy using base class copy constructor Ø Again, Shallow Copy! • BDP: Overload the default assignment operator if deep copy is required § Applies to both copy constructor and copy assignment operator
Section 3 Copy – Swap Idiom
Copy-Swap Idiom Ø Consider the following copy assignment scenario using deep copy • • • A new local copy instance is implicitly instantiated in the assignment function, including pointer member and the referenced data Non-pointer members are copied over from the source (pass by reference) //Pseudocode Example Pointer member of the local copy instance is first deallocated (why? ) my. C& operator= (const my. C &m){ New memory is allocated with the right size & type based on the source pointer member Base: : operator=(m); this->simple_ = m. simple; The values from the pointer member is copied from the source to the local copy instance this->comp_ = m. comp_; The reference of the local copy instance is returned to the assignment destination delete this->ptr_; this->ptr_ = new type[m. psize]; memmove(this->ptr, m. ptr_, m. size*sizeof(type)); Ø Problem? • • If exception occurs when allocating the data content, the reference of the local copy instance be pointed to a corrupted memory section that’s deleted by default Toxic Pointer – a reference to deleted memory section (BAD!) Source (Pb. R) Base comp_ ptr_ simple_ return *this; } Local Copy Base comp_ Referenced Data simple_Referenced Data Base comp_ ptr_ simple_ Referenced Data
Copy-Swap Idiom Ø Solution – Copy-Swap Idiom • • • Change the input parameter from Pb. R to Pb. V (m now a copy of source on stack) Copy the non-pointer members from source to local copy instance Swap the pointers in the source and the local copy instance The reference of the local copy instance is returned to the assignment destination The modified copy of the source on the stack is destroyed upon return //Pseudocode Example my. C& operator= (my. C m){ Base: : operator=(m); this->simple_ = m. simple; this->comp_ = m. comp_; Ø Advantage? • swap(this->prt_, m. prt_); Source is copied through copy constructor on to stack first § When exception thrown in copy constructor, destructor is automatically invoked § No chance to have toxic pointer Source Copy (Pb. V) Local Copy Base comp_ ptr_ simple_ Base comp_ simple_Referenced Data return *this; } Referenced Data
Copy-Swap Idiom Ø BDP: Deploy copy-swap approach in assignment operator function whenever pointer members are present Ø Can even be simplified further //Pseudocode Example my. C& operator= (my. C m){ Base: : operator=(m); this->simple_ = m. simple; this->comp_ = m. comp_; swap(this->prt_, m. prt_); Ø Beware • • • STL Library Usage Can also deploy your own swap mechanism Performance tradeoff return *this; } //Pseudocode Example 2 my. C& operator= (my. C m){ swap(*this, m); return *this; }
Section 4 Move Constructor and Move Assignment
Move Constructor Ø A move constructor construct a new object whose value is equal to an existing object Ø But DOES NOT preserve the value of the existing object (why? ) Ø The following C++ statement would normally invoke the copy constructor to copy ADT instances class Money; Money foo 2(); //An ADT //A function returning ADT int main(){ Money m; Money n = foo 2(); } //Calls constructor //Calls Move Constructor upon foo 2() return
Move Constructor Ø A move constructor construct a new object whose value is equal to an existing object • But DOES NOT preserve the value of the existing object Ø How to overload a move constructor in an ADT class (notice the rvalue reference) class Money{ public: Money(); ~Money(); //An ADT //constructor //destructor Money(Money &&m){ //move constructor amount_ = m. amount_; //copy the value over m. amount_ = 37416782; //Does not matter if source contents changed } private: int amount_; }
Default Move Constructor Ø The compiler-generated version of the default move constructor has the following member-wise move characteristics • • Simple data members (primitive types) Pointer members Member objects Inherited members Question: Is deep copy required? Bitwise copy Copy using member move/copy constructor Copy using base class move/copy constructor Original Base comp_ Copy Base Shallow comp_ ptr_ simple_ Copy Referenced Data
Move Assignment Operator Ø Similar to move constructor, except the destination of the move already exists • And AGAIN DOES NOT preserve the source integrity (why? ) Ø The following C++ statement would normally invoke the copy assignment operator class Money; int main(){ Money m, p; p = Money(); } //An ADT //Calls constructor THEN move assignment operator
Move Assignment Operator Ø Similar to move constructor, except the destination of the move already exists Ø How to overload a move assignment operator in an ADT class (the rvalue reference) class Money{ public: Money(); ~Money(); //An ADT //constructor //destructor Money& operator= (Money &&m){ this -> amount_ = m. amount_; m. amount_ = 37416782; return *this; } private: int amount_; } //move assignment //copy the value over //Does not matter //return the copied ADT
Move Assignment Operator Ø The compiler-generated version of the default move assignment operator has the following member-wise move assignment characteristics • • Simple data members (primitive types) Pointer members Member objects Inherited members Ø Again, Shallow Copy! • Answer: no deep copy required • Copy-swap as needed Bitwise copy Copy using member move/copy constructor Copy using base class move/copy constructor
Section 5 Rule of 5, Equality
Special Member Function Table • Notice that when 1 of the SMFs is defined, there are chances for other SMFs to become undefined / deleted by default. • BDP: Rule of 5 • If you have overloaded one of the default special member functions, you should overload the remaining 5.
Equality Operator (and other Logic Ops) Ø Shallow Equality Comparand 1 Comparand 2 Base comp_ ptr_ simple_ Referenced Data Ø Deep Equality Comparand 1 Referenced Data Comparand 2 Base comp_ ptr_ simple_ Referenced Data Ø No compiler default equality operator (and other logic operators). We are on our own.
Equality Operator Ø Example Pseudocode class my. C; bool operator== (const my. C &m, const my. C &n){ if( !( Base: : operator==(m, n) ) ) return false; if( m. simple_ != n. simple_ ) return false; if( !( m. comp_ == n. comp_ ) ) return false; //assuming != operator not overloaded if( !m. ptr_ && !n. ptr_ ) return true; //no pointer member, no further comparison required if( !m. ptr_ || !n. ptr_ ) return false; //if one comparand does not have a pointer member… return *m. ptr_ == *n. ptr_; }
Upgrade our Rational ADT Ø Let’s deploy the 6 member functions for our Rational ADT with Pimpl Ø You should try implementing the deep equality algorithm for the logic operators yourself
Summary
Summary Ø C++ Special Member Functions (6 of them) • BDP: “Rule of 5” If one needs custom definition, likely all of them do • BDP: Know what’s in the default compiler-generated special member functions, and overload them whenever required – particularly Deep Copy is required over Shallow Copy Ø When the Special Member Functions are called? • Sometimes compiler optimization can play evil Ø Pay attention to the details of the overload signatures of special member functions • lvalue v. s. rvalue
- Slides: 33