ECE 150 Fundamentals of Programming Issues with classes

  • Slides: 22
Download presentation
ECE 150 Fundamentals of Programming Issues with classes Prof. Hiren Patel, Ph. D. Douglas

ECE 150 Fundamentals of Programming Issues with classes Prof. Hiren Patel, Ph. D. Douglas Wilhelm Harder, M. Math. LEL hdpatel@uwaterloo. ca dwharder@uwaterloo. ca © © 2018 by by Douglas Wilhelm Harder and Hiren Patel.

Issueswithclasses Outline • In this lesson, we will: – Describe problems with the default

Issueswithclasses Outline • In this lesson, we will: – Describe problems with the default behavior of copying or assigning of objects – See that in addition to initialization, we may require cleanup – Introduce the • Copy constructor • Assignment operator • Destructor

Issueswithclasses Constructor and assignment • By default, – A constructor can be used to

Issueswithclasses Constructor and assignment • By default, – A constructor can be used to initialize an object – If you create a copy of an object, all member variables are copied – Assignment copies all of the member variables – When a local object goes out of scope, the memory is cleaned up – When a dynamically allocated object is deleted, the memory is returned to the heap

Issueswithclasses Constructor and assignment • For example, all three local objects behave as expected:

Issueswithclasses Constructor and assignment • For example, all three local objects behave as expected: // Class declarations class Vector_3 d; // Function declarations int main(); // Function definitions int main() { Vector_3 d u{1, 2, 3}; Vector_3 d v{u}; Vector_3 d w{0, 0, 0}; w = u; Output: (1. 000000, 2. 000000, 3. 000000) std: : cout << u. to_string() << std: : endl; std: : cout << v. to_string() << std: : endl; std: : cout << w. to_string() << std: : endl; return 0; }

Issueswithclasses Constructor and assignment • The behavior is the same for dynamically allocated objects:

Issueswithclasses Constructor and assignment • The behavior is the same for dynamically allocated objects: // Function definitions int main() { Vector_3 d *p_u = new Vector_3 d{1, 2, 3}; Vector_3 d *p_v = new Vector_3 d{*p_u}; Vector_3 d *p_w = new Vector_3 d{0, 0, 0}; *p_w = *p_u; std: : cout << p_u->to_string() << std: : endl; std: : cout << p_v->to_string() << std: : endl; std: : cout << p_w->to_string() << std: : endl; delete p_u; delete p_v; delete p_w; return 0; } Output: (1. 000000, 2. 000000, 3. 000000)

Issueswithclasses The idea • Consider the following problem: – Each object should have a

Issueswithclasses The idea • Consider the following problem: – Each object should have a unique ID that must stay unchanged – Each object has a value that may change – We want a count of the number of instances in existence • Now, we have problems: – When a copy is made: • A new ID must be created • The value should be copied over • The instance count should be increased – When an assignment is performed: • Only the value should be copied over – When an instance goes out of scope or is deleted • The number of instances should be decreased

Issueswithclasses The idea • Here is our class definition: class Container { public: Container(

Issueswithclasses The idea • Here is our class definition: class Container { public: Container( double value ); std: : string to_string() const; static int instance_count(); private: double value_; int id_; static int instance_count_; static int next_id_; }; int Container: : instance_count_{0}; int Container: : next_id_{20000000};

Issueswithclasses The idea • Here are the definitions of the member functions: Container: :

Issueswithclasses The idea • Here are the definitions of the member functions: Container: : Container( double value ): value_{value}, id_{next_id_} { ++next_id_; ++instance_count_; } std: : string Container: : to_string() const { return std: : to_string( id_ ) + ": " + std: : to_string( value_ ); } static int Container: : instance_count() { return instance_count_; }

Issueswithclasses Incorrect behavior • If we execute this code the output is wrong: int

Issueswithclasses Incorrect behavior • If we execute this code the output is wrong: int main() { Container a{32. 5}; std: : cout << a. to_string() << std: : endl; { Container std: : cout c = a; std: : cout b{a}; << b. to_string() << c{75. 2}; << c. to_string() << Output: 20000000: 32. 500000 20000001: 75. 20000000: 32. 500000 std: : endl; Instance count: 2 std: : endl; << c. to_string() << std: : endl; std: : cout << "Instance count: " << Container: : instance_count() << std: : endl; } std: : cout << "Instance count: " << Container: : instance_count() << std: : endl; return 0; }

Issueswithclasses The copy constructor • We must define a new constructor that is called

Issueswithclasses The copy constructor • We must define a new constructor that is called when copying: class Container { public: Container( double value ); Container( Container const &original ); std: : string to_string() const; static int instance_count(); private: double value_; int id_; static int instance_count_; static int next_id_; }; 1

Issueswithclasses The copy constructor • In this case, the copy constructor must: – Generate

Issueswithclasses The copy constructor • In this case, the copy constructor must: – Generate a new ID – Increase the instance count – Copy over the value from the original Container: : Container( Container const &original ): value_{original. value_}, // Gets the value from 'original' id_{next_id_} { // Get a new ID ++next_id_; // Increases the ID ++instance_count_; // Increases the count } 1

Issueswithclasses The assignment operator • We must define a new assignment operator that is

Issueswithclasses The assignment operator • We must define a new assignment operator that is called when performing an assignment: class Container { public: Container( double value ); Container( Container const &original ); Container &operator=( Container const &rhs ); std: : string to_string() const; static int instance_count(); private: double value_; int id_; static int instance_count_; static int next_id_; }; 1

Issueswithclasses The assignment operator • In this case, the assignment operator must: – Leave

Issueswithclasses The assignment operator • In this case, the assignment operator must: – Leave the ID and instant count unchanged – Copy over the value from the right hand side – Return a reference to the object (i. e. , return *this) Container &Container: : operator=( Container const &rhs ) { // Copy the value and leave the ID and count unchanged value_ = rhs. value_; return *this; } 1

Issueswithclasses The destructor • Whenever a object goes out of scope or is deleted,

Issueswithclasses The destructor • Whenever a object goes out of scope or is deleted, we must call a destructor: class Container { public: Container( double value ); Container( Container const &original ); ~Container(); Container &operator=( Container const &rhs ); std: : string to_string() const; static int instance_count(); private: double value_; int id_; static int instance_count_; static int next_id_; }; 1

Issueswithclasses The destructor • In this case, the destructor must: – Decrease the instance

Issueswithclasses The destructor • In this case, the destructor must: – Decrease the instance count Container: : ~Container() { --instance_count_; } 1

Issueswithclasses Correct behavior • If we now execute the same code: int Output: 20000000:

Issueswithclasses Correct behavior • If we now execute the same code: int Output: 20000000: 32. 500000 main() { Container a{32. 5}; 20000001: 32. 500000 std: : cout << a. to_string() << std: : endl; 20000002: 75. 20000002: 32. 500000 { Instance count: 3 Container b{a}; Instance count: 1 std: : cout << b. to_string() << std: : endl; Container c{75. 2}; std: : cout << c. to_string() << std: : endl; c = a; std: : cout << c. to_string() << std: : endl; std: : cout << "Instance count: " << Container: : instance_count() << std: : endl; } std: : cout << "Instance count: " << Container: : instance_count() << std: : endl; return 0; } 1

Issueswithclasses Behavior • The copy constructor’s argument is always an instance of the class

Issueswithclasses Behavior • The copy constructor’s argument is always an instance of the class passed by constant reference: Class_name( Class_name const &original ); • The assignment operator’s argument is always an instance of the class passed by constant reference and it returns *this: Class_name &operator=( Class_name const &rhs ); • The destructor is always the class name with a ~ and it never takes any arguments: ~Class_name(); 1

Issueswithclasses Looking ahead • Next, we will look at two classes that require all

Issueswithclasses Looking ahead • Next, we will look at two classes that require all of the functionality described here and where the default behavior would be wrong: – An array class – A linked list class 1

Issueswithclasses Summary • Following this lesson, you now – Understand how classes can be

Issueswithclasses Summary • Following this lesson, you now – Understand how classes can be used to hide member variables – Know that one or constructors can be used to initialize member variables • Like functions, they can have different arguments • They are called either during variable declaration or during calls to the new operator – Know that a destructor can be defined to clean up after an object is deallocated • They are called either when a variable goes out of scope or during calls to the delete operator 1

Issueswithclasses References [1] No references? 2

Issueswithclasses References [1] No references? 2

Issueswithclasses Colophon These slides were prepared using the Georgia typeface. Mathematical equations use Times

Issueswithclasses Colophon These slides were prepared using the Georgia typeface. Mathematical equations use Times New Roman, and source code is presented using Consolas. The photographs of lilacs in bloom appearing on the title slide and accenting the top of each other slide were taken at the Royal Botanical Gardens on May 27, 2018 by Douglas Wilhelm Harder. Please see https: //www. rbg. ca/ for more information. 2

Issueswithclasses Disclaimer These slides are provided for the ECE 150 Fundamentals of Programming course

Issueswithclasses Disclaimer These slides are provided for the ECE 150 Fundamentals of Programming course taught at the University of Waterloo. The material in it reflects the authors’ best judgment in light of the information available to them at the time of preparation. Any reliance on these course slides by any party for any other purpose are the responsibility of such parties. The authors accept no responsibility for damages, if any, suffered by any party as a result of decisions made or actions based on these course slides for any other purpose than that for which it was intended. 2