590 likes | 724 Views
Copy Control. Joe Meehean. More Class Responsibilities. When making a new type (i.e., class) we must specify what happens when it is: Copied Assigned Destroyed. Copy Constructor. Used to create a new copy of a class object Takes a single parameter of same class type (usually const )
E N D
Copy Control Joe Meehean
More Class Responsibilities • When making a new type (i.e., class) we must specify what happens when it is: • Copied • Assigned • Destroyed
Copy Constructor • Used to create a new copy of a class object • Takes a single parameter of same class type (usually const) • Has no return type • Declaration syntax • Class(const Class& orig) • e.g., IntMatrix(constIntMatrix& orig);
Copy Constructor • Usage syntax • Class varname(origname) • e.g., IntMatrix a(4,4);IntMatrix b(a); • Definition requirements • must initialize all data members • data members should match values of original’s data members
Copy Constructor // (header) .h classHitSong{ Song hit_song_; intchart_rank_; intsingles_sold_; HitSong( Song& song, intchart_rank, intsingles_sold); HitSong( constHitSong& orig); };
Copy Constructor HitSong::HitSong(constHitSong& orig) : hit_song_(orig.hit_song_), chart_rank_(orig.chart_rank_), singles_sold_(orig.singles_sold_) { // nothing to do here } • Note: this constructor can access private data of orig • Because both this and orig are instances of HitSong
Copy Constructor • What if we don’t write a copy constructor? • Compiler creates one for us • called the synthesized copy constructor • created even if we define other constructors • Synthesized copy constructor • performs memberwise initialization of member data • initializes each member variable by copying original member variable • if member variable is a built-in (primitive), just copies the bits • if member variable is a class, uses the class’s copy constructor • if member variable is an array, copies each element
Copy Constructor • How do we prevent copies of our class? • Approach 1: make copy constructor private • prevents non-class code from making copies • friends and member functions of class can still make copies • Approach 2: make undefined, private copy constructor • declare it • e.g., IntMatrix(constIntMatrix& orig); • do not define it • perfectly legal • non-class code gets compiler errors • class code and friends get linking errors
Copy Constructor • When is it used • Explicitly in code • e.g., IntMatrix b(a); • Passing class instance as an argument to a function • non-reference • e.g., flip(IntMatrix a); • Returning a non-reference instance from a function • e.g., return a;
Copy Constructor • When is it used • Explicitly initializing an array Song songs[] = { Song(“Simply Irresistable”, “Robert Palmer”), Song(“The Power of Love”, “Huey Lewis & The News”), Song(“In the Air Tonight”, “Phil Collins”) }; • Uses constructor to create temporary Songs • Initializes array using copies of temporary Songs
Copy Constructor • Classes best practices: always define a • default constructor • copy constructor
Assignment Operator • If you implement a class,you need to control what happens during an assignment • e.g., b = a; • make sure data is copied correctly from a to b • How do we write a function for an operator?
Assignment Operator • How do we write a function for an operator? • syntax: return_type operator symbol(type operand1, type operand 2, …) • e.g.,int operator + ( constint& a, constint& b); • The assignment operator (=) often returns a reference to the assigned variable • allows: a = b = c; • e.g.,int& operator = (int& a, int& b); • More on this in future lectures
Assignment Operator • When we write an operator function for a class,the first parameter is implicitly the this parameter • Assignment operator for a class declaration syntax • Class& operator =(const Class& rhs) • e.g., HitSong& operator =(constHitSong& rhs) • Assignment operator for a class definition syntax • Class& Class::operator =(const Class& rhs){ … } • e.g., HitSong& HitSong::operator =(constHitSong& rhs)
Assignment Operator • When doing assignment we are copying values from one instance of a class to another • unlike copy constructor, both instances are already initialized Song aSong(“Twilight Zone”, “Golden Earring”); Song bSong(“Radar Love”, “Golden Earring”); aSong = bSong;
Assignment Operator HitSong& HitSong::operator =(constHitSong& rhs) { if( this != &rhs ){ this->song_ = rhs.song_; this->chart_rank_ = rhs.chart_rank_; this->singles_sold_ = rhs.singles_sold_; } return *this; }
Assignment Operator HitSong& HitSong::operator =(constHitSong& rhs) { if( this != &rhs ){ this->song_ = rhs.song_; this->chart_rank_ = rhs.chart_rank_; this->singles_sold_ = rhs.singles_sold_; } return *this; }
Assignment Operator HitSong& HitSong::operator =(constHitSong& rhs) { if( this != &rhs ){ this->song_ = rhs.song_; this->chart_rank_ = rhs.chart_rank_; this->singles_sold_ = rhs.singles_sold_; } return *this; }
Assignment Operator • What if we don’t write an assignment operator? • Compiler makes a synthesized assignment operator • Performs memberwise assignment • each member variable is assigned member variable from rhs • built-ins use a bitwise assignment • classes uses their own assignment operator • arrays are assigned element by element • Returns *this
Assignment Operator • What happens in this code? Song aSong(“Hotel California”, “Eagles”); Song b = aSong;
Assignment Operator • What happens in this code? Song aSong(“Hotel California”, “Eagles”); Song b = aSong; • b is created using Song’s copy constructor
Assignment Operator • Classes best practices: always define a • default constructor • copy constructor • assignment operator
Destructor • How do we clean up the memory we allocated with the constructor? • Each class needs a destructor method • one more responsibility of class creator • specifies how to clean up memory allocated for member data • complement of constructors
Destructor • When is the destructor called? • Automatic objects • automatically destroyed when they go out of scope intHitSong::func(){ Song aSong(“Could well be in”, “The Streets”); // other stuff ... } aSong is destroyed using its destructor
Destructor • When is the destructor called? • Dynamically allocated objects • destroyed explicitly using delete intHitSong::func(){ Song * pSong = new Song( “Could well be in”, “The Streets”); // other stuff ... delete pSong; } Memory pointed to by pSong is destroyed using its destructor
Destructor • When is the destructor called? • Dynamically allocated objects • if never deleted, it is never destroyed intHitSong::func(){ Song * pSong = new Song( “Could well be in”, “The Streets”); // other stuff ... } pSong lives on forever. We cannot get access to it either.
Destructor • When is the destructor called? • Arrays of objects • deleted when array is destroyed intHitSong::func(){ Song songs[10]; // other stuff ... } each Song in songs is destroyed using its destructor
Destructor • Declaration syntax • virtual ~Class(); • e.g., virtual ~Song(); • takes no parameters • has not return type • ignore what virtual means for now, just include it • Definition syntax • Class::~Class(){ • e.g., Song::~Song(){
Destructor • Usage syntax • how do we call the destructor • you don’t, its called automatically in cases described earlier
Destructor • What happens if we don’t define one? • Compiler defines a destructor for you. • destroys each non-static member variable • using same rules like the object went out of scope • destroys them in reverse order of declaration • What happens if we do define one • compiler defines one even if you define one too • compiler runs its destructor after yours
Destructor • Why bother to define a destructor if the compiler already does it for you? • May need to deallocate non-automatic resources • dynamically allocated memory • close open files • amongst others (more on this in OS) • What should our destructor do? • if you don’t dynamically allocate memory (or other resources) • nothing
Destructor • Classes best practices: always define a • default constructor • copy constructor • assignment operator • destructor • Actually… • you should decide for yourself, except • Big Three: copy constructor, assignment operator, and destructor • if you define a one of Big Three, should always define other two
Copy Control and Pointers • Pointers make copy control (Big Three) more difficult • Class designer must make one of two choices: • Shallow copy • copy and assignment should copy only the pointer • Deep copy • copy and assignment allocate new memory and copy object pointer points to
Copy Control and Pointers • Assume Song::Song( const char* name,const char *artist){ name_ = new char[512]; // SHOULD USE STRLEN artist_ = new char[512]; strcpy(name_, name); strcpy(artist_, artist); } void Song::setArtist(const char* artist){ strcpy(artist_, artist); // SHOULD CHECK LENGTH } void Song::deleteName(){ delete name_; name_ = NULL; }
Copy Control and Pointers • Shallow copying Song::Song(const Song& orig){ name_ = orig.name_; artist_ = orig.artist_; }
Copy Control and Pointers • Shallow copying void main(){ Song aSong(“All along the watchtower”, “Bob Dylan”); } aSong : Bob Dylan artist_ name_ All along…
Copy Control and Pointers • Shallow copying void main(){ Song aSong(“All along the watchtower”, “Bob Dylan”); Song bSong(aSong); } bSong : aSong : Bob Dylan artist_ artist_ name_ name_ All along…
Copy Control and Pointers • Shallow copying void main(){ Song aSong(“All along the watchtower”, “Bob Dylan”); Song bSong(aSong); aSong.setArtist(“Jimi Hendrix”); } bSong : aSong : Jimi Hendrix artist_ artist_ name_ name_ All along…
Copy Control and Pointers • Shallow copying void main(){ Song aSong(“All along the watchtower”, “Bob Dylan”); Song bSong(aSong); aSong.setArtist(“Jimi Hendrix”); aSong.deleteName(); } bSong : aSong : Jimi Hendrix artist_ artist_ name_ name_ All along… Director’s Cut
Copy Control and Pointers • Dangers of shallow copies • data modified out from under you • data destroyed, left pointing at garbage • program crashes or acts strangely
Copy Control and Pointers • Deep copying Song::Song(constSong& orig){ name_ = new char[strlen(orig.name_)+1]; artist_ = new char[strlen(orig.artist_)+1]; strcpy(name_, orig.name_); strcpy(artist_, orig.artist_); }
Copy Control and Pointers • Deep copying void main(){ Song aSong(“All along the watchtower”, “Bob Dylan”); } aSong : Bob Dylan artist_ name_ All along…
Copy Control and Pointers • Deep copying void main(){ Song aSong(“All along the watchtower”, “Bob Dylan”); Song bSong(aSong); } bSong : aSong : Bob Dylan Bob Dylan artist_ artist_ name_ name_ All along… All along…
Copy Control and Pointers • Deep copying void main(){ Song aSong(“All along the watchtower”, “Bob Dylan”); Song bSong(aSong); aSong.setArtist(“Jimi Hendrix”); } bSong : aSong : Jimi Hendrix Bob Dylan artist_ artist_ name_ name_ All along… All along…
Copy Control and Pointers • Deep copying void main(){ Song aSong(“All along the watchtower”, “Bob Dylan”); Song bSong(aSong); aSong.setArtist(“Jimi Hendrix”); aSong.deleteName(); } bSong : aSong : Jimi Hendrix Bob Dylan artist_ artist_ name_ name_ All along… All along… Director’s Cut
Copy Control and Pointers • If you don’t define a copy constructor or an assignment operator • synthesized versions perform a shallow copy • If you define a deep copy constructor and assignment operator, but not a destructor • you will leak memory
Copy Control and Pointers • Deep copying Song::operator =(const Song& orig){ if( this != &orig ){ delete name_; delete artist_; name_ = new char[strlen(orig.name_)+1]; artist_ = new char[strlen(orig.artist_)+1]; strcpy(name_, orig.name_); strcpy(artist_, orig.artist_); } return *this; }