1 / 31

Creating Temperature Class - Reviewing Class Operations

A review of the operations involved in creating a Temperature class to model temperatures, including input, conversion, and accessor functions.

Télécharger la présentation

Creating Temperature Class - Reviewing Class Operations

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Class Operations Creating New Types

  2. Review Last time, we began building Temperature, a class to allow us to model temperatures: class Temperature { public: Temperature(); Temperature(double magnitude, char scale); void Print(ostream & out) const; private: double myMagnitude; char myScale; };

  3. Review We saw that classes have: • data members, for storing class attributes; and • function members, for operating on class objects. Function members are like messages sent to an object -- when an object receives a message, that object performs the statements in the function’s definition.

  4. Input It is useful to be able to input a Temperature. From the internal perspective, we can specify this task as follows. Specification: Receive: in, an istream. Precondition: in contains valid magnitude and scale values. Input: the magnitude and scale values from in. Passback: in, minus its magnitude and scale values. Postcondition: myMagnitude == magnitude && myScale == scale.

  5. Input Prototype class Temperature { public: Temperature(); Temperature(double magnitude, char scale); void Read(istream & in); void Print(ostream & out) const; private: double myMagnitude; char myScale; }; Unlike output, the input operation changes the class data members, and so is not a const function.

  6. Input Definition void Temperature::Read(istream & in) { double magnitude; char scale; in >> magnitude >> scale; if (islower(scale)) scale = toupper(scale); assert(in.good() && scale == ‘C’ || scale == ‘F’); myMagnitude = magnitude; myScale = scale; } Input is an easy place for errors to occur, so always carefully check the preconditions of an input function.

  7. Testing // ... documentation // ... other #includes #include “Temperature.h” int main() { cout << “\nEnter a temperature: “; Temperature temp1; temp1.Read(cin); // read it temp1.Print(cout); // echo it back cout << endl; } To test this function, we can write:

  8. Conversion Functions To find out the celsius equivalent of a class object, we want to be able to send it the Celsius() message. Temperature temp1; // ... Temperature temp2 = temp1.Celsius(); From the internal perspective, our specification is: Specification: Return: the Celsius equivalent of myself.

  9. Celsius() Prototype class Temperature { public: Temperature(); Temperature(double magnitude, char scale); Temperature Celsius() const; void Read(istream & in); void Print(ostream & out) const; private: double myMagnitude; char myScale; }; This operation will not alter the class data members, and so should be declared a const function.

  10. Celsius() Definition Temperature Temperature::Celsius() const { switch (myScale) { case ‘C’: return Temperature(myMagnitude, ‘C’); case ‘F’: return Temperature((myMagnitude - 32)/1.8, ‘F’); default: cerr << “\nInvalid scale: “ << myScale << “ in Celsius().\n” << endl; exit(1); } }

  11. Celsius() Definition Temperature Temperature::Celsius() const { switch (myScale) { case ‘C’: return Temperature(myMagnitude, ‘C’); case ‘F’: return Temperature((myMagnitude - 32)/1.8, ‘F’); default: cerr << “\nInvalid scale: “ << myScale << “ in Celsius().\n” << endl; exit(1); } } Using our second Temperature constructor, we can build our return-value dynamically!

  12. Testing #include “Temperature.h” int main() { cout << “\nEnter a temperature: “; Temperature temp1, temp2; temp1.Read(cin); // read temp2 = temp1.Celsius(); // convert temp2.Print(cout); // output cout << endl; } To test this function, we can write:

  13. Testing #include “Temperature.h” int main() { cout << “\nEnter a temperature: “; Temperature temp1; temp1.Read(cin); temp1.Celsius().Print(cout); cout << endl; } Alternatively, we can chain messages: This sends temp1 the Celsius() message, returning a Temperature, to which we send a Print() message.

  14. Fahrenheit() Definition The Fahrenheit() definition and prototype are similar to those of Celsius(): Temperature Temperature::Fahrenheit() const { switch (myScale) { case ‘F’: return Temperature(myMagnitude, ‘F’); case ‘C’: return Temperature(myMagnitude * 1.8 + 32, ‘C’); default: cerr << “\nInvalid scale: “ << myScale << “ in Fahrenheit().\n” << endl; exit(1); } }

  15. Fahrenheit() Prototype class Temperature { public: Temperature(); Temperature(double magnitude, char scale); Temperature Celsius() const; Temperature Fahrenheit() const; void Read(istream & in); void Print(ostream & out) const; private: double myMagnitude; char myScale; };

  16. Accessor Functions To find out the magnitude or scale of a temperature object, we want to be able to send it the Magnitude() or Scale() messages. Temperature temp1; // ... cout << “Its magnitude is “ << temp1.Magnitude() << “ and its scale is “ << temp1.Scale() << endl; Such functions are called accessor functions, since they access (retrieve) the values of data members.

  17. Accessor Prototypes class Temperature { public: Temperature(); Temperature(double magnitude, char scale); double Magnitude() const; char Scale() const; Temperature Celsius() const; void Read(istream & in); void Print(ostream & out) const; private: double myMagnitude; char myScale; }; Why are these declared as const functions?

  18. Accessor Definitions The Scale() and Magnitude() functions are similar: char Temperature::Scale() const { return myScale; } double Temperature::Magnitude() const { return myMagnitude; }

  19. Accessor Definitions The Scale() and Magnitude() functions are similar: inline char Temperature::Scale() const { return myScale; } inline double Temperature::Magnitude() const { return myMagnitude; } Functions this simple can be defined in the header file, provided they are declared as inline functions, which • eliminates function-call overhead; and • eliminates multiple-definition linking errors.

  20. The Extraction Operator Temperature temp1; temp1.Read(cin); Instead of writing: it would be convenient to be able to write: Temperature temp1; cin >> temp1; Since the left operand is not a Temperature (and we are unable to modify the istream class), we cannot implement this operation as a function member!

  21. Extraction Definition Thanks to our Read() member, this is easy to define, by sending parameter temp the Read() message: istream & operator>>(istream & in, Temperature & temp) { temp.Read(in); return in; } The function must return the actual istream it receives via in, but the normal function-return mechanism makes a copy of what is returned.

  22. Extraction Definition Thanks to our Read() member, this is easy to define, by sending parameter temp the Read() message: istream & operator>>(istream & in, Temperature & temp) { temp.Read(in); return in; } The function must return the actual istream it receives via in, but the normal function-return mechanism makes a copy of what is returned. This copying mechanism can be turned off by making the function’s return-type a reference.

  23. Extractor Prototype class Temperature { public: Temperature(); // ... other prototypes omitted void Read(istream & in); void Print(ostream & out) const; friend istream & operator>>(istream & in, Temperature & temp); private: double myMagnitude; char myScale; }; Non-function-member operations can be declared within the class by naming them as friends of the class:

  24. Testing #include “Temperature.h” int main() { cout << “\nEnter a temperature: “; Temperature temp1, temp2; cin >> temp1; // read temp2 = temp1.Celsius(); // convert temp2.Print(cout); // output cout << endl; } To test this function, we can write:

  25. The Insertion Operator temp1.Print(cout); Instead of writing: it would be convenient to be able to write: cout << temp1 << endl; Since the left operand is not a Temperature (and we are unable to modify the ostream class), we cannot implement this operation as a function member, either!

  26. Extraction Definition Thanks to our Print() member, this is easy to define, by sending parameter temp the Print() message: ostream & operator<<(ostream & out, const Temperature & temp) { temp.Print(out); return out; } In general, the name operatorD can be used to overload the operator whose symbol is D. Since this function does not alter its parameter temp, but is a class object, it should be declared as a const reference parameter.

  27. Extractor Prototype class Temperature { public: // ... other prototypes omitted friend istream & operator>>(istream & in, Temperature & temp); friend ostream & operator<<(ostream & out, const Temperature & out); private: // ... data members omitted }; We then have the class name operator<< as a friend:

  28. Testing #include “Temperature.h” int main() { cout << “\nEnter a temperature: “; Temperature temp1, temp2; cin >> temp1; // read temp2 = temp1.Celsius(); // convert cout << temp2 << endl; // output } To test this function, we can now write:

  29. Testing #include “Temperature.h” int main() { cout << “\nEnter a temperature: “; Temperature temp1, temp2; cin >> temp1; // read temp2 = temp1.Celsius(); // convert cout << temp2 << endl; // output } To test this function, we can now write: The reason operator<< returns an ostream & is to allow output expressions to be chained.

  30. Summary C++ classes enable a programmer to define new types that provide rich sets of operations. Function members that do not modify class data members should be declared as const functions. Trivial function members can be defined in the header file, so long as they are declared as inline functions. A class can grant non-function-members access to the private data by naming them as friends of the class.

  31. Summary (Ct’d) There are many more STL algorithms beyond the ones presented here (we barely scratched the surface). For a complete list, see the most recent edition of “The C++ Programming Language”, by Bjarne Stroustrup, Addison-Wesley.

More Related