Agenda / Learning Objectives: 1. Run the following command and - - PDF document

agenda learning objectives
SMART_READER_LITE
LIVE PREVIEW

Agenda / Learning Objectives: 1. Run the following command and - - PDF document

Agenda / Learning Objectives: 1. Run the following command and extract lab14.tar in your venus account (note the dot): cp ~ctse/cs211/lab14.tar . ; tar xvf lab14.tar 2. Understand the logic from lines 81 to 83 in Ch8Prog6.cpp. 3. Read the tips


slide-1
SLIDE 1

Agenda / Learning Objectives:

  • 1. Run the following command and extract lab14.tar in your venus

account (note the dot): cp ~ctse/cs211/lab14.tar . ; tar xvf lab14.tar

  • 2. Understand the logic from lines 81 to 83 in Ch8Prog6.cpp.
  • 3. Read the tips and pitfalls starting on page 2.
  • 4. Complete the following exercise from the textbook.

Review class lectures with Chapter 8 question 2: Rational number class

Define a class for rational numbers. A rational number is a number that can be represented as the quotient of two integers. For example, 1/2, 3/4, 64/2, and so forth are all rational numbers. (By 1/2 and so on we mean the everyday fraction, not the integer division this expression would produce in a C++ program.) Represent rational numbers as two values of type int, one for the numerator and

  • ne for the denominator. Call the class Rational. Include a constructor with two arguments that

can be used to set the member variables of an object to any legitimate values. Also include a constructor that has only a single parameter of type int; call this single parameter wholeNumber and define the constructor so that the object will be initialized to the rational number wholeNumber/1. Include a default constructor that initializes an object to 0 (that is, to 0/1). Overload the input and output operators >> and <<. Numbers are to be input and output in the form 1/2, 15/32, 300/401, and so forth. Note that the numerator, the denominator, or both may contain a minus sign, so −1/2, 15/−32, and −300/−401 are also possible inputs. Overload all the following operators so that they correctly apply to the type Rational: ==, <, <=, >, >=, +, −, *, and /. Write a test program to test your class. Hints: Two rational numbers a/b and c/d are equal if a*d equals c*b. If b and d are positive rational numbers, a/b is less than c/d provided a*d is less than c*b. You should include a function to normalize the values stored so that, after normalization, the denominator is positive and the numerator and denominator are as small as possible. For example, after normalization 4/-8 would be represented the same as −1/2.

slide-2
SLIDE 2

From teacher’s note for Absolute C++:

Pitfalls

Overloading &&, || and the Comma Operator. It is safest not to overload these operators as the

  • verloaded || and && operators perform complete evaluation instead of short-circuit evaluation while

the overloaded comma operator does not guarantee left to right evaluation. Member Operators and Automatic Type Conversion. When you overload a binary operator as a member operator, the two arguments are no longer symmetric. One is a calling object and only the second argument is a true argument. Overloaded member operators behave more nicely; see the tip on Member vs. Friend Overloading. Compilers without Friends. On some compilers, friend functions simply do not work as they are supposed to. If this happens then you must use accessor functions to define nonmember functions and

  • verloaded operators or you must overload operators as members. All of the latest compilers do seem

to support friends.

Key Points

Overloading Basics for Operators. With a small syntax change we can overload the default operators. Recall that few operations (+, /, -, ==, <, etc.) apply to class objects directly. We do have assignment, =, that results in member wise copy. We have seen that frequently this should be called member-UN-wise copy, since under circumstances frequently encountered, this results in a disaster. To prevent mischief, C++ requires that at least one argument of an overloaded operator be a class

  • bject. Operators are overloaded using an “operator function” having its name made up of the keyword
  • perator followed by the operator symbol to be overloaded. The operator function must be a member of

a class (in which case the calling object is an argument for the operator) or the operator function must have a class object as at least one of its arguments. Most operators can be overloaded, only a few cannot. Specifically, the operators ., .* , ?: , and :: may not be overloaded. There are a few operators that must be overloaded as non-static member functions, namely =, ->, (), []. When an operator is overloaded, only operators that exist can be overloaded: No new tokens can be

  • formed. For example, you cannot create an exponentiation operator ** as in Fortran. Only the behavior
  • f operators can be changed by overloading. The arity of an operator cannot be changed. Arity (the

number of arguments an operator takes, that is, whether the operator is binary or unary) and the precedence of an operator cannot be changed by overloading. For example, the “not” operator, !, is a unary prefix operator and cannot be made into a binary operator by overloading. Overloading as Member Functions. When a binary operator is overloaded as a member function, the

  • perator’s left argument is always the calling object. The operator function must have one argument in

this case. Having the operator function as member is convenient for access to class internals, but it prevents a measure of flexibility in symmetric treatment of overloading. When a binary operator is overloaded as a stand-alone function, the operator function must have two

  • arguments. The left-hand operand of the expression is the first argument for the operator function and

the right-hand operand of the expression is the second argument for the operator function.

slide-3
SLIDE 3

Overloading Unary Operators. When a unary operator is overloaded as a member function, the only

  • perand is the calling object. This is true regardless of whether the operator is prefix or postfix. The

compiler will take care of that detail. (But see the discussion of overloading the postfix ++ operator, later.) When a unary operator is overloaded as a stand-alone function, the argument of the operator is the argument for the operator function. For example, class A; //A is defined somewhere else A a; !a . . . //compiler translates this into operator!(a) //for a stand-alone overloaded operator Overloading Function Application (). Overloading this operator implements function call syntax for class objects. It can have default arguments. If we overload using class T { public: . . . void operator()(T t); . . . }; Where T1 and T2 are classes, then the function calls syntax, T v, w; u(v); is translated into a function call u.operator()(v); Given a call in a setting where there are several overloaded operator functions that may match, the “best match” rules for function overload resolution are used to find the operator function to be used. Friend Functions and Automatic Type Conversion. A friend function has access to all members of a class, regardless of access control (public, protected or private). A constructor with one argument provides automatic type conversion. A friend of a class is not a member. The access keywords public, private or protected have no effect on friend a declaration, so where in the class you place the declaration is immaterial. Constructors for Automatic Type Conversion. If you use a different type than the expected type in a function call, the compiler will look for a conversion from the type provided to the expected type. Constructors having one argument are candidates for this conversion. This conversion is applied to arguments for overloaded operator functions as well as to other functions. Friend Classes. Sometimes, there is a need for all functions of one class to be friends of another class. Declaring class F to be a friend of a class C makes each member functions of be F a friend of class C. There is often a choice between declaring a class into a friend of another class and making the class a member of the second class. Clearly, classes should be made friends only when the two classes represent closely related entities in the problem being solved.

  • References. A reference is another name, that is, an alias, for an object. A reference is very nearly an

automatically dereferenced, constant pointer, but pointers and references are not interchangeable. The main use for references is passing parameters to functions, function return values, and for overloaded

  • perators. If T is a type, the expression T& x; defines x to be a reference to a type T object, and requires
  • initialization. We do not care for use of stand-alone references, nevertheless, we write the following as

an illustration. int i;

slide-4
SLIDE 4

int& intRef = i; Any use of intRef as an l-value or as an r-value will have the same effect as using the int variable i. Since use of these standalone references mostly provides opportunity for error, we weight in against of this use. When we use a reference variable as a parameter, the function call mechanism initializes the reference to refer to the caller’s argument (which must be an l-value.) Overloading >> and <<. If we are the class author, we can put a member overloading

  • perator <<(ourclass object)

in our class definition. The istream and ostream classes are not ours to modify. It would not be desirable for us to do so, even if we could, since every one else will want her (or his) class to have i/o in the iostream library. The answer is to write a stand-alone operator function that overloads our inserter or extractor, where the iostream is the first argument, and our class object is the second argument. This provides us with what we need: in_stream >> our_class_object; and

  • ut_stream << our_class_object;

When we overload operator << for output, the return value is a reference to the output stream so that we can have chain of output statements. Similarly, when we overload operator >> for input, the return value is a reference to the input stream so that we can have chain of input statements. The Assignment Operator. If no operator= is provided, the compiler will generate an operator= that copies members. If you have only primitive values for class variables, this is what you want. Otherwise, the programmer has to write an overloaded opreator=. The assignment operator, operator=, must be

  • verloaded as a non-static class member. The text waits until pointers are discussed before treating
  • verloading the assignment operator.

Overloading the Array Operator []. The index operator, [], must be overloaded as a non-static member function of the class. When overloading the index operator, the text says that the parameter must be an integer type. This is a simplification. In fact, any type can be used. This enables associative arrays (that the text does not treat). Overloading based on l-value versus r-value. The technique for distinguishing between use of a returned value as an l-value from use its use as an r-value was sought for some time during the development of C++. There are times when it is necessary to make this distinction. For real examples, see the implementation of the indexing operators in the Standard Template Library.

Tips

A Constructor Can Return an Object. We often think of constructors as void functions, but constructors create an object and can also be thought of as returning an object of the class. An expression like: Money(dollars, cents).getDollars() is the creation of an anonymous

  • bject whose getDollars function is invoked.

Returning Member Variables of a Class Type. When returning a member variable of a class type, in almost all cases it is important to return the member value by const. Otherwise, the caller will be able to modify the contents of a potentially private member variable. A Class Has Access to All Its Objects. When defining a member function or operator, you may access any private member variable or function of the calling object.

slide-5
SLIDE 5

When to Use Friend Functions. When should an operation be a friend function and when is making the function a member the better choice for an operation? Actually, the question should be what is the minimum access that still does the job. Some operations must be members, things like constructors, destructors, and certain overloaded operators. Typically, there is a choice for most of the remainder of the functions we need to write. The idea is to make the set of functions with access to the class as small as possible. Experts advise not using friends except to avoid global data, global (non-member functions)

  • r public data members.

Member versus friend overloading. There is a compelling argument in favor of overloading operators using friends rather than overloading as members. If both operands are function arguments, then C++ will do automatic type conversion of either argument to the class type from simple types such as int (provided you have supplied an appropriate constructor). This allows you to use 2 as a Rational (see Programming problem 2) or an amount of money in an expression such as

  • bject + 2
  • r

2 + object which are converted to

  • perator+( object, 2 );
  • r
  • perator+( 2, object );

where the non class type arguments will be converted to an object using the constructor having an int

  • argument. However, if operator+ is a member, then the first argument must always be an object, as in:
  • bject + 2

and expressions such as 2 + object are always illegal. This is the reason for presenting the friend rather than member operator overloading first. Operator Overloading using void return type. While the text always overloads the << and >>

  • perators to return a stream reference, some instructors prefer to arrange an operator overloading to
  • utput to a stream from a class to return a void than to return a stream reference. While this provides

somewhat simpler code in the implementation, the result violates the philosophy of the language design. It is the intent of the language design that an operator overloaded for a class should behave as it does for predefined types. Whether the operator function returns a stream reference or a void is a matter of whether we want to have the following style of i/o available to us:

  • ur_class x, y, z;

in_stream >> x >> y >> z; If we are content to write instead, in_stream >> x; in_stream >> y; in_stream >> z; The difference in coding amounts to replacing the return type with void, and removing the return statement from the function. The sole advantage is not having to return the stream. // returns istream&: istream& operator >> (istream& ins, Money& amount) { //all the necessary code to fetch the amount //from the istream and do some format checking //are in Display 8.8 in the text. return ins; }

slide-6
SLIDE 6

void operator >> (istream& ins, Money& amount) { //all the necessary code to fetch the amount //from the istream and do some format checking //are in Display 8.8 in the text. //no return statement... } Similar arrangements work for overloading operator <<. Overloading the increment and decrement Operators. If ++ or -- is overloaded as a unary operator, the operator becomes a prefix operator. To get a postfix operator overloading, you have to treat the

  • perator overloading as if it were a binary operator with an int second argument. For example:

// operator overloaded as a member class A { public: // other members A operator++(int); // . . . }; A A::operator++(int); // operator overloaded as a friend. class A { public: // other members friend A operator++(A&, int); //. . . }; A operator++(A& a, int i) { //code to carry out the increment } When the postfix-overloaded operator is used, it is illegal to put an int value following the postfix ++ or

  • - operator. If you can call the operator function directly, you must put an int argument in for the int

parameter.