C++ data types • 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. Classes Similarities 1. same syntax 2. Both used to model objects with different attributes (characteristics) represented as data members (also called fields or instance variables). Thus, both used to process non-homogeneous data sets. Structs vs. Classes 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 Members of a class by default are private cannot be accessed outside the struct but can be explicitly declared public C++ Classes C++'s structs and classes model objects that have: • Attributes (characteristics) represented as data members • Operations (behaviors) represented as function members (methods). classes new style of programming : Object-Oriented Objects are self-contained, Associated with their own operations 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. Common Forms of Declaring a Class class ClassName { // private by default Declarations of private members public: Declarations of public members }; class ClassName { public: //"interface" of class given 1st Declarations of public members private: Declarations of private members }; Notes on Declaring a Class 1. Data members are normally placed in the private section of a class; Function members in the public section. 2. Some programmers prefer to put the private section first Use the default access for classes Omit the private: specifier 3. Other programmers put the public interface of the class first and the hidden private details last. (In the book, we use the latter approach) 4. Although not commonly done (poor technique), a class may have several private and public sections; the keywords private: and public: mark the beginning of each. Access to Class Members A particular instance of a class is called an object : ClassName object_name; Private members can be accessed only within the class (except by friend functions to be described later). Public members can be accessed within or outside the class. to access them outside the class, one must use the dot operator: object_name.public_member_name Class declarations are placed in a header file whose name is ClassName.h Header file for class Time — Version 1 /** Time.h ----defines the data type Time with operations Set: To set the time Display: To display the time */ #include <iostream> using namespace std; class Time { /******** Member functions ********/ public: /* sets data members of(Time)object to specified values * Receive: hours, minutes (in standard time) * AMPM ('A' if AM, 'P' if PM) */ void Set(unsigned hours, unsigned minutes, char am_pm); Header file for class Time — Continued /* Display displays time in standard and military format * using output stream out. * * Receive: ostream out * Output: The time represented by Time object * for which this function invoked ***************************/ // const function: cannot modify data members // good practice: protects data members from // accidental modification. void Display(ostream & out) const; private: // Data Members unsigned myHours, myMinutes; char myAMorPM; // 'A' or 'P' unsigned myMilTime; // military time equivalent }; // end of class declaration Data Members: Why private? "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 program need not know about implementation! Implementation may change 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 Implementation of a Class Class declaration contains Declaration of data members Prototypes (declaration) of member functions Member functions are not usually placed in class declaration clutters up the interface definitions placed outside class header must tell compiler where the declaration/prototype is Use the scope operator :: which has the form ClassName::ItemName (the qualified or full name of ItemName.) Implementation of class Time — Version 1 // Time.cpp -- implements the Time member functions #include "Time.h" /* ToMilitary converts standard time to military time. Receive: hours, minutes, am_pm Return: The military time equivalent Could implement this utility function as 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); } void Time::Display(ostream & out) const { out << myHours << ':' << (myMinutes < 10 ? "0" : "") << myMinutes << ' ' << myAMorPM << ".M. (" << myMilTime << " mil. time)"; } Implementation of class Time — Continued void Time::Set(unsigned hours, unsigned minutes, char am_pm) { // Check class invariant if (hours >= 1 && hours <= 12 && minutes >= 0 && minutes <= 59 && (am_pm == 'A' || am_pm == 'P')) { myHours = hours; myMinutes = minutes; myAMorPM = am_pm; myMilTime = ToMilitary(hours, minutes, am_pm); } else cerr << "*** Can't set time with these values ***\n"; // Object's data members remain unchanged } Test driver for class Time #include "Time.h" #include <iostream> using namespace std; int main() { Time mealTime; mealTime.Set(5, 30, 'P'); cout << "We'll be eating at "; mealTime.Display(cout); cout << endl; } Execution We'll be eating at 5:30 P.M. (1730 mil. time) Object-Oriented Perspective Procedural: send object off to some function for processing OO: 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 . Class Constructors constructing an object consists of: allocating memory for the object, and initializing the object. In our example, after the declaration Time mealTime; memory has been allocated for object mealTime but it's data members are not initialized (likely to contain "garbage" values). Need to: specify initial values for mealTime provide default values to be used if no initial values are specified. Class Constructors - Properties names are always the same as the class name. initialize the data members of an object with default values or values provided as arguments do not return a value - have no return type (not even void). often simple and can be inlined called whenever an object is declared. 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; Constructors for Time class // Time.h class Time { public: /** Construct a class object (default). * Precondition: A Time object has been declared. * Postcondition: Time object is initialized to 12:00 A.M.; **********************/ Time(); /* --- Construct a class object (explicit values). * Precondition: A Time object has been declared. * Receive: Initial values initHours, initMinutes, and * initAMPM * Postcondition: Time object are initialized to passed values **********************/ Time(unsigned initHours, unsigned initMinutes, char initAMPM); . . . // other member function prototypes private: /********** Data Members **********/ . . . }; // end of class declaration Constructors for Time class - Implementation inline Time::Time() { myHours = 12; myMinutes = 0; myAMorPM = 'A'; myMilTime = 0; } Time::Time(unsigned initHours, unsigned initMinutes, char initAMPM) { // Check class invariant assert(initHours >= 1 && initHours <= 12 && initMinutes >= 0 && initMinutes <= 59 && (initAMPM == 'A' || initAMPM == 'P')); myHours = initHours; myMinutes = initMinutes; myAMorPM = initAMPM; myMilTime = ToMilitary(initHours, initMinutes, initAMPM); } Create and initializes 2 Time objects Time mealTime, //default constructor bedTime(11,30,'P'); //explicit-value constructor Code: mealTime.Display(cout); cout << endl; bedTime.Display(cout); cout << endl; Execution: 12:00 A.M. (0 mil. time) 11:30 P.M. (2330 mil. time) Constructors for Time class - Default Parameters Can 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. Initial values initHours, initMinutes, and Receive: initAMPM (defaults 12, 0, 'A') Postcondition: Time object initialized to initHours, initMinutes, and initAMPM , respectively. ---*/ Time(unsigned initHours = 12, unsigned initMinutes = 0, char initAMPM = 'A'); Note: Any parameter with default argument must appear after all parameters without default arguments. Copy Operations Two default copy operations are provided: 1. Copy in initialization (copy constructor) 2. Copy in assignment (assignment operator) Each makes a (byte-by-byte) copy of the data members of the object. Called a bitwise or memberwise copy. Examples: Time t = bedTime; // copy constructor called Time t(bedTime); // copy constructor called Both: 1. Allocate memory for t 2. 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 (via the assignment operator) into t. Access Member Functions Data members are private: they cannot be accessed outside the class. To make the values stored in some or all of these members accessible, provide access member functions Example: provide access to myHours of class Time. (access for the remaining members is analogous.) simply retrieves the value stored in a data member inline prototype (and define) as a const function // add to class Time inline unsigned Time::Hour() const; { return myHours; } Output and Input Add output operation(s) to a class early use for debugging. Example: overload operator<< for a Time object cout << "We'll be eating at " << mealTime << endl; instead of cout << "We'll be eating at " ; mealTime.Display(cout); cout << endl; Overloading operators In C++, operator can be implemented with the function operator() — If a member function of a class C, and a is of type C, the compiler treats a b as a.operator(b) — If not a member function of a class C, the compiler treats a b as operator(a, b) Overloading Output Operator << Can operator<<() be a member function? No, because the compiler will treat cout << t as cout.operator<<(t) which means that operator<<() is a member of class ostream! Putting the prototype ostream& operator<<(ostream& out, const Time& t); inside the class declaration causes a compiler error like: 'Time::operator <<(ostream &, const Time &)' must take exactly one argument operator<<() as a member function of Time means that it has the Time object as an (implicit) parameter, so it needs only one formal parameter. //If simple, inline definition in.h file inline ostream& operator<<(ostream& out, const Time& t) { t.Display(out); return out; } Why?? out a reference parameter : ostream gets modified so must be passed back t a const reference parameter : avoid overhead of copying a class object return a reference to out: chain output operators cout << t1 << endl << t2 << endl; << left-associative => operator<<(cout, t1) << endl << t2 << endl; first function must return cout => cout << endl << t2 << endl; => operator<<(cout, endl) << t2 << endl; Alternative: declare operator<< to be a friend of class Time //in header file friend ostream& operator<<(ostream& out, const Time& t); //in implementation file ostream & operator<<(ostream & out, const Time & t) { out << t.myHours << ':' << (t.myMinutes < 10 ? "0" : "") << t.myMinutes << ' ' << t.myAMorPM << ".M. (" << t.myMilTime << " mil. time)"; return out; } Friend Functions A function that a class declares as a friend is a non-member function to which the class has granted permission to access members in its private sections. Note: Because a friend function is not a function member: • definition not qualified with class name and scope operator (::) • receives the object on which it operates as a parameter • uses the dot operator to access the data members. Relational Operator< Specification: Receives: Two Time objects Returns: True if the first Time object is less than the second; false otherwise. Should operator< be a member function? internal perspective: I compare myself with another Time object and determine if I am less than that other object external perspective: Two Time objects are compared to determine if the first is less than the second. OOP "I-can-do-it-myself" principle ( objects self-contained) use member functions whenever possible. Rephrased specification: Receives: A Time object (and the current object implicitly) Returns: True if I (the Time object containing this function) am less than the Time object received; false otherwise. Relational Operator< // 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<(const Time & t); ... }; // end of class declaration inline bool Time::operator<(const Time & t) { return myMilTime < t.myMilTime; } Caveat: Operator Overloading Internal perspective may be too restrictive for completely consistent use e.g. for some objects a & b of some class PromoteD a < b; b < a; // ok: operator syntax, // equivalent to a.operator<(b) // ok, same as b.operator(a) a < 2; // ok, given a constructor that // promotes 2 to object of typepromoteD // Statement then equivalent to a.operator<(2) 2 < a; // ERROR. 2.operator<(a) has no meaning Syntactically confusing to application programmer to support a < 2 but disallow 2 < a. use friends Overloaded Operator as Friend external 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 Time::operator<(const Time& t1, const Time& t2); ... }; // end of class declaration inline bool operator<(const Time& t1, const Time& t2) { return t1.myMilTime < t2.myMilTime; } Redundant Declarations class Time might be used in programs, libraries, etc. 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" cause "redeclaration" errors during compiling. How do we prevent the declarations in Time.h from being included more than once in a file? Conditional Compilation Wrap the declarations in Time.h inside preprocessor directives #ifndef TIME Usually 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 defined: Proceed to second directive ( define TIME ) Continue to the #endif and beyond. If not defined: Preprocessor removes all code that follows until a #elsif, #else, of #endif directive encountered.