Presentation is loading. Please wait.

Presentation is loading. Please wait.

Class Operations Creating New Types. Review Last time, we began building, a class to allow us to model temperatures: Last time, we began building Temperature,

Similar presentations


Presentation on theme: "Class Operations Creating New Types. Review Last time, we began building, a class to allow us to model temperatures: Last time, we began building Temperature,"— Presentation transcript:

1 Class Operations Creating New Types

2 Review Last time, we began building, a class to allow us to model temperatures: 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. 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. myScale == scale.

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

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 To test this function, we can write: //... 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; }

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 This operation will not alter the class data members, and so should be declared a function. This operation will not alter the class data members, and so should be declared a const function. 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; };

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 constructor, we can build our return-value dynamically! Using our second Temperature constructor, we can build our return-value dynamically!

12 Testing To test this function, we can write: #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; }

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

14 Fahrenheit() Definition 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); } The definition and prototype are similar to those of : The Fahrenheit() definition and prototype are similar to those of Celsius() :

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 or messages. 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 Why are these declared as functions? Why are these declared as const functions? 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; };

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

19 Accessor Definitions inline double Temperature::Magnitude() const { return myMagnitude; } The and functions are similar: The Scale() and Magnitude() functions are similar: inline char Temperature::Scale() const { return myScale; } 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 Instead of writing: Temperature temp1; temp1.Read(cin); Since the left operand is not a (and we are unable to modify the istream class), we cannot implement this operation as a function member! 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! it would be convenient to be able to write: Temperature temp1; cin >> temp1;

21 Extraction Definition istream & operator>>(istream & in, Temperature & temp) { temp.Read(in); return in; } Thanks to our member, this is easy to define, by sending parameter the message: Thanks to our Read() member, this is easy to define, by sending parameter temp the Read() message: The function must return the actual istream it receives via, but the normal function-return mechanism makes a copy of what is returned. 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 istream & operator>>(istream & in, Temperature & temp) { temp.Read(in); return in; } Thanks to our member, this is easy to define, by sending parameter the message: Thanks to our Read() member, this is easy to define, by sending parameter temp the Read() message: The function must return the actual istream it receives via, but the normal function-return mechanism makes a copy of what is returned. 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 Non-function-member operations can be declared within the class by naming them as friends of the class: 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; };

24 Testing To test this function, we can write: #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; }

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

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

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

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


Download ppt "Class Operations Creating New Types. Review Last time, we began building, a class to allow us to model temperatures: Last time, we began building Temperature,"

Similar presentations


Ads by Google