03 Classes

52
1 3. More About Data Structures and ADTs: C++ Data Types (Read §3.1 & §3.2) C++ types an be classified as: Fundamental (or simple or scalar): A data object of one of these types is a single object. - int, double, char, bool, complex, and the related types (unsigned, short, etc.) - enumerations Structured: Collections of data - arrays, structs, unions, classes, valarrays, bitsets, the containers and adapters in STL

description

Classes

Transcript of 03 Classes

  • 3. More About Data Structures and ADTs:C++ Data Types (Read 3.1 & 3.2)C++ types an be classified as:

    Fundamental (or simple or scalar): A data object of one of these types is a single object. - int, double, char, bool, complex, and the related types (unsigned, short, etc.) - enumerations

    Structured: Collections of data arrays, structs, unions, classes, valarrays, bitsets, the containers and adapters in STL

  • Structs vs. ClassesSimilarities1. Essentially the same syntax2. Both are used to model objects with multiple attributes (characteristics) represented as data members (also called fields or instance or attribute variables). Thus, both are used to process non-homogeneous data sets.

  • Differences

    1. C does not provide classes; C++ provides both structs and classes 2. Members of a struct by default are public (can be accessed outside the struct by using the dot operator.) In C++ they can be declared to be private (cannot be accessed outside the struct.)

    3. Members of a class by default are private (cannot be accessed outside the class) but can be explicitly declared to be public.

  • C Structs vs. C++ Classes (& Structs)("traditional" vs "OOP")C++'s structs and classes model objects that have: Attributes (characteristics) represented as andAttributesData MembersOperationsFunction MembersThis leads to a whole new style of programming: object-oriented. Objects are self-contained,possessing their own operations commonly called the I can do it myself principle rather than being passed as a parameter to an external function that operates on them and sends them back. Operations (behaviors) represented as (also called methods).data membersfunction members

  • Declaring a ClassCommon Forms:class ClassName{// private by defaultDeclarations of private members public:Declarations of public members};

    class ClassName{ public://"interface" of class given firstDeclarations of public members private:Declarations of private members};

  • Some programmers prefer to put the private section first:- Using the default access for classes - Can omit the private: specifier

    Other programmers put the public interface of the class first and the hidden private details last. (In the text, the latter approach is used.)

    Although not commonly done, a class may have several private and public sections; the keywords private: and public: mark the beginning of each.AttributesData MembersOperationsFunction Memberspublic:private:Notes:

    Data members are normally placed in the private section of a class; function members in the public section.

  • Access to Class MembersA particular instance of a class is called an object: ClassName object_name;Private members can be accessed only within the class (object) (except by friend functions to be described later).Public members can be accessed within or outside the class (object); to access them outside the class (object), one must use the dot operator: object_name.public_member_name A class declaration is usually placed in a header file whose name is ClassName.h . The library is then called a class library.

  • Header file for class Time Version 1/** Time.h -------------------------------------------------------- This header file defines the data type Time for processing time. Basic operations are: Set: To set the time Display: To display the time ----------------------------------------------------------------------*/#include using namespace std;class Time { /******** Member functions ********/ public:/* Set sets the data members of a Time object to specified values. * * Receive: hours, the number of hours in standard time * minutes, the number of minutes in standard time * AMPM ('A' if AM, 'P' if PM) * Postcondition: The Time object containing this function has its * myHours, myMinutes, and myAMorPM members set to hours, * minutes, and am_pm, respectively, and myMilTime to * the equivalent military time ********************************************************************/void Set(unsigned hours, unsigned minutes, char am_pm);Plus: Name, Course #, Lab/Project #, etc.

  • /* Display displays time in standard and military format using * output stream out. * * Receive: ostream out * Output: The time represented by the Time object containing * this function * Passes back: The ostream out with time inserted into it *****************************************************************/Notes: 1. "my" in data members names is a reminder of internal ("I can do it myself") perspective.2. const at end of Display()'s prototype makes it a const function which means it cannot modify any of the data members. Good idea to protect data members from accidental modification.void Display(ostream & out) const;

    /********** Data Members **********/private: unsigned myHours, myMinutes; char myAMorPM; // 'A' or 'P' unsigned myMilTime; // military time equivalent }; // end of class declaration"Accessors" vs."Mutators"

  • 3. Why make data members private? (p. 91)

    "Hidden" data members: Cannot be accessed outside class Application programs must interact with an object through its interface Public member functions control interaction between programs and class. Application programs need not know about implementation! Implementation may change (improve storage, simpler algorithms. etc.) If interface is constant, programs using an object need not be changed. What's wrong with tying application code to implementation details? A change in implementation forces a change in the application code Increased upgrade time Increased programmer cost Decreased programmer productivity Reduced profits due to Delayed releases/upgrades Loss of customer confidence in software reliability Always define data members of a class as private.

  • Implementation of a ClassClass declaration contains: Declarations of data members Prototypes (declarations) of function members Definitions of function members are not usually placed in class declaration Avoid cluttering up the interface Definitions placed outside the class declaration must tell compiler where the corresponding declaration/prototype is : Use the scope operator :: which has the form ClassName::ItemName (the qualified or full name of ItemName.)This applies also to constants and types declared within a class.e.g., Lab 3

  • class Something { public: static const int CAPACITY = 100; typedef double ArrayType[CAPACITY]; void Print(ArrayType a, int itsSize); . . . };See slide 19Example: . . . Something::ArrayType x = {0};for (int i = 0; i < Something::CAPACITY; i++) . . .void Something::Print(Something::ArrayType a, int itsSize) { . . . }Use of static locals inside functions

  • Traditionally, definitions of member functions have been put in an implementation file ClassName.cpp corresponding to the class' header file. This is done to enforce data abstraction separating the interface of the ADT from its implementation details. (Unfortunately, the class data members, which store data and are therefore part of the implementation, must be in the .h file.)With the increasing use of templates, however, this practice is becoming less common because current compiler technology doesn't permit this split for templates everything has to be in the same file. Thus the reason for dropping the ".h" from standard class libraries. They're really class-template libraries, and there are therefore no corresponding ".cpp" files.

  • Implementation of class Time Version 1// Time.cpp -- implements the Time member functions #include "Time.h"

    /*** Utility functions ***/

    /* ToMilitary converts standard time to military time. Receive: hours, minutes, am_pm Return: The military time equivalent Could implement this as a private class method*/

    int ToMilitary (unsigned hours, unsigned minutes, char am_pm){ if (hours == 12) hours = 0; return hours * 100 + minutes + (am_pm == 'P' ? 1200 : 0);}Note the doc

  • Implementation of class Time cont.//--- Function to implement the Set operation

    void Time::Set(unsigned hours, unsigned minutes, char am_pm){ // Check class invariant if (hours >= 1 && hours = 0 && minutes

  • Implementation of class Time cont.//--- Function to implement the Display operation

    void Time::Display(ostream & out) const{ out

  • Test driver for class Time#include "Time.h" #include using namespace std; int main() {Time mealTime;mealTime.Set(5, 30, 'P'); cout
  • Object-Oriented PerspectiveProcedural:Send object off to some function for processingOOP: Send a message to the object to operate on itself. To set my digital watch to 5:30 P.M., I don't wrap it up and mail it off to Casio. I push a button! To display the time on my watch, I don't wrap it up and mail it off to Casio and have them tell me what time it is. I have it display the time itself, perhaps pushing a button to turn on the backlight so I can see it .

  • Notes:1. Member functions: "Inside" an object, so don't pass object to them as a parameter. (They receive the object to be operated on implicitly, rather than explicitly via a parameter.) Non-member functions: "Outside" an object, so to operate on an object, they must receive it via a parameter.2. Public items must be qualified when referred to outside the class declaration: ClassName::ItemName Public constants are usually declared static so they are global class properties that can be accessed by all objects of that class type rather than each object having its own copy.3.Simple member functions: Usually specified as inline functions.This suggests to compiler to replace a function call with actual code of the function with parameters replaced by arguments saves overhead of function call.Danger: "Code Bloat"

  • Two ways to inline a class function member:1.Prototype in class declaration; inline definition below the class declaration in the header file, qualifying the name as usual.In ClassName.h: class ClassName { public: RetType SimpleFun(param_list); //prototype . . . }; inline RetType ClassName::SimpleFun(param_list) { . . . } // definition2.Put functions definition (instead of prototype) inside class declaration compiler will treat it as inlined.Interfaceclutter

  • Class Invariant: A condition (boolean expression) that ensures that the data members always contain valid values.Example:1 < myHours < 12 && 0 < myMinutes < 59 && myAMorPM == 'A' or 'P' && 0 < myMilTime < 2359 Operations that modify data members must check the class invariant.Then other operations can be sure data members have valid values.2. Use assert(class_invar); (must #include )truefalseHow?1. Use an if statement see Set()continue executionhalt execution and display error messageMutators

  • 3. Throw an exception that the calling function can catch and take appropriate action://----- Function to implement the Set operation ----- void Time::Set(unsigned hours, unsigned minutes, char am_pm) { // Check class invariant if (hours >= 1 && hours = 0 && minutes
  • To catch this exception, a calling function might contain:try { mealTime.Set(13, 30, 'P'); cout
  • Class ConstructorsConstructing an object consists of:(1) allocating memory for the object, and(2) initializing the object.In our example, after the declaration Time mealTime; memory has been allocated for object mealTime and it's data members have some default (perhaps "garbage") initial values. Need to: Specify initial values for mealTime Provide default values to be used if no initial values are specified.This is the role of a class' constructor. (Later, it will also allocate memory.)

  • Class Constructors - Properties6. If no constructor is given in the class, compiler supplies a default constructor which allocates memory and initializes it with some default (possibly garbage) value.A default constructor is one that is used when the declaration of an object contains no initial values: ClassName object_name;An explicit-value constructor is used for declarations with initial values: ClassName object_name(list-of-init-values);7. If we supply a class constructor, we must also provide a default constructor or we can't use first kind of declaration.1. Names are always the same as the class name.2. Initialize the data members of an object with default values or with values provided as arguments.3. Do not return a value; have no return type (not even void).4. Often simple and can be inlined.5. Called whenever an object is declared.

  • Constructors for Time classIn Time.hclass Time { public: /* --- Construct a class object (default). * Precondition: A Time object has been declared. * Postcondition: Data members initialized to 12, 0, 'A', and 0. ****************************************************************/ Time(); /* --- Construct a class object (explicit values). * Precondition: A Time object has been declared. * Receive: Initial values initHours, initMinutes,and * initAMPM * Postcondition: Data members initialized to initHours,initMinutes, * initAMPM, & correspoding military time **********************************************************/ Time(unsigned initHours, unsigned initMinutes, char initAMPM); . . . // other member function prototypes private: /********** Data Members **********/ . . . }; // end of class declaration

  • Constructors for Time class - ImplementationIn Time.h, after class declaration: inline Time::Time() { myHours = 12; myMinutes = 0; myAMorPM = 'A'; myMilTime = 0; }In Time.cpp: Time::Time(unsigned initHours, unsigned initMinutes, char initAMPM){ // Check class invariant assert(initHours >= 1 && initHours = 0 && initMinutes
  • Testing #1:Time mealTime, //default constructor bedTime(11,30,'P'); //explicit-value constructorCreate and initialize 2 Time objects:mealTime.Display(cout); cout
  • Constructors for Time class Default ArgumentsCan combine both constructors into a single constructor function by using default arguments:Replace constructors in Time.h with:/*--- Construct a class object. Precondition: A Time object has been declared. Receive: Initial values initHours, initMinutes, and initAMPM (defaults 12, 0, 'A') Postcondition: Data members initialized to initHours, initMinutes, initAMPM, & correspoding military time. */

    Time(unsigned initHours = 12, unsigned initMinutes = 0, char initAMPM = 'A');

    Note: Any parameter with default argument must appear after all parameters without default arguments.App. 1 from"Other Links"

  • Testing:Time mealTime, t1(5), t2(5, 30), t3(5, 30, 'P');Creates 4 Time objects:mealTime.Display(cout); cout
  • Copy OperationsTwo default copy operations are provided:1. Copy in initialization (via )2. Copy in assignment (via )copy constructorassignment operatorEach makes a raw (bitwise) copy of the memory allocated to the data members of the object.This is OK for now

  • Examples:Time t = bedTime;Time t(bedTime);Both:1. Allocate memory for t2.Copy data members of bedTime so t is a copy of bedTime In contrast:Time t = Time(11, 30, 'P');calls the explicit-value constructor to construct a (temporary) Time object and then copies it into t.Note: These are not assignments; a defaultcopy constructor is called.What about Time t = 3; ?In mixed-mode operations, compiler will look for a constructor to convert one value to the type of the other.

    bedTime

    myHours

    myMinutes

    myAMorPM

    myMilTime

    Function members

    t

    myHours

    myMinutes

    myAMorPM

    myMilTime

    Function members

  • There is a default copy operation for assignment. Example:t = mealTime;copies the members of mealTime into t, replacing any previous values:120A0120A0It returns this Time object as the value of this expression.This is OK for nowWhy?So we can chain =

    mealTime

    myHours

    myMinutes

    myAMorPM

    myMilTime

    Function members

    t

    myHours

    myMinutes

    myAMorPM

    myMilTime

    Function members

  • Access Member FunctionsData members are private: they cannot be accessed outside the class. To make the values stored in some or all of these members accessible, provide accessor (or extractor) member functions. (They are non-mutators; mutators change data members.)Example: To provide access to myHours of class Time. (Access for the remaining members is analogous.) simply retrieves and returns the value stored in a data member inline, because it's simple prototype (and define) as a const function Add in Time class declaration /* Hour Accessor * Return: value stored in myHours data member of * Time object containing this function */unsigned Hour() const;

  • Add below Time class declaration

    inline unsigned Time::Hour() const{ return myHours; }Testing:Time mealTime; . . .cout

  • Output and Input Add output operation(s) to a class early so it can be used for debugging other operations.Example: overload operator
  • Overloading operatorsIn C++, operator D can be implemented with the function operatorD(). If a member function of a class C, and a is of type C, the compiler treats a D b as a.operatorD(b)If not a member function of a class C, the compiler treats a D b as operatorD(a, b)

  • Overloading Output Operator
  • Why is out a reference parameter? So corresponding actual ostream argument gets modified when out does.

    Why is t a const reference parameter? Avoid overhead of copying a class object.

    Why is return type ostream & (a reference to an ostream)?Else a copy of out is returned.

    Why return out? So we can chain output operators.Since

  • . . . } // end of class declaration . . . /* operator
  • Method 2: Define operator
  • Friend FunctionsA function that a class declares as a friend is a non-member function to which the class has grantedpermission to access members in its private sections.

    Note:Because a friend function is not a function member: Don't qualify its definition with class name and scope operator (::). Don't put friend in definition. It receives the object on which it operates as a parameter. It uses the dot operator to access the data members.

  • To add an input operator to our Time class, we proceed in much the same way as for output. We could either:1. Add a member function ReadTime() that reads values and stores them in the data members of a Time object; then call it from non-member function operator>>()

    2. Declare operator>>() to be a friend function so that it can access the data members of a Time object and store input values in them.Is one of two methods for input/output preferred?We'll see later when we study inheritance and polymorphism that the first method is preferred (or perhaps required).

  • Relational Operators (
  • // Internal perspective : Add to Time.h

    class Time { public: // member functions . . . /***** Relational operators *****/ /* operator< determines if one Time is less than another Time * Receive: A Time t (and the current object implicitly) * Return: True if time represented by current object is < t. */bool operator

  • Caveat re Operator OverloadingInternal perspective may lead to seeming inconsistencies:

    Example: Suppose a and b are objects of type class C.a < b okay?Yes, equivalent to a.operator

  • Overloaded Operator as FriendExternal perspective: (Permits a < 2 and 2 < a , if 2 can be promoted)class Time { public: // member functions . . . /* operator< * Receive: Two Times t1 and t2 * Return: True if time t1 is less than time t2/ */friend bool operator
  • Adding Increment/Decrement OperatorsSpecification: Receives:A Time object (perhaps implicitly) Returns: The Time object with minutes incremented by 1 minute.

    Question:Should it be a member function? Add to Time.h: /***** Increment operator *****/ /* --- Advance() increments a Time by 1 minute. Postcondition: The Time object has its minutes incremented by 1. -----------------------------------------------*/ void Advance();Yes

  • Add to Time.cpp:

    //----- Function to implement Advance() -----void Time::Advance() { myMinutes++; myHours += myMinutes / 60; myMinutes %= 60; myHours %= 12; if (myMilTime == 1159) myAMorPM = 'P'; else if (myMilTime == 2359) myAMorPM = 'A'; // else no change myMilTime = ToMilitary(myHours, myMinutes, myAMorPM); }

  • We could replace Advance() with overloaded operator++().Question: How do we distinguish between prefix ++ and postfix ++?++operator++() with no parametersoperator++(int) with one int parameter (which is not used in the definition.)Postfix ++:What's the difference?Solution: In C++, when the Compiler encounters:It looks for: Prefix ++:

  • Redundant DeclarationsClass Time might be used in different programs, libraries, etc., so it could be included several times in the same file e.g., Program needs Time class, so it #includes "Time.h" Program also needs library Lib, so it #includes "Lib.h" . . . but Lib.h also includes "Time.h This can cause "redeclaration" errors during compiling. How do we prevent the declarations in Time.h from being included more than once in a file?

  • Conditional CompilationWrap the declarations in Time.h inside preprocessor directives. (Preprocessor scans through file removing comments, #including files, and processing other directives (which begin with #) before the file is passed to the compiler.) #ifndef TIMEUsually the name of the class in all caps #define TIME: : #endif

    The first directive tests to see whether the identifier TIME has been defined. If not defined: Proceed to second directive ( define TIME ) Continue to the #endif and beyond.

    If defined: Preprocessor removes all code that follows until a #elsif, #else, or #endif directive encountered.