430 likes | 584 Views
HKUST Summer Programming Course 2008. Class ~ The essence of Object Oriented Programming. Overview. Difference between struct and class Abstract data type Class components Object Oriented Programming Why object oriented? OOP Features Data Abstraction Encapsulation / Information Hiding
E N D
HKUST SummerProgramming Course 2008 Class ~ The essence of Object Oriented Programming
Overview • Difference between struct and class • Abstract data type • Class components • Object Oriented Programming • Why object oriented? • OOP Features • Data Abstraction • Encapsulation / Information Hiding • Class Declaration and Class Definition
Overview • Class Name: Name Equivalence • Class Data Members • Forward Declaration • Initialization • Class Member Functions • Constant Member Functions • Inline Functions • Member Access Control • How are Objects Implemented? • “this” Pointer • Class Scope and Scope Operator :: • Static Constant Data Member • Class Object Initialization
Difference between struct and class • The only difference between class and struct: • In struct, variables and function are “public” if you don’t specify. • In class, they are “private” by default.
Abstract data type • int, char, double and others are pre-defined data types. On the other hand, class is a user-defined type or an abstract data type. • Variable of a class, is called an “object”. For example, we have a class called Fruit, then we can define an object “apple” of this type. Fruit apple; • Object apple is said to be an “instance” of the Fruit class. Class (Abstract Data Type) Object (Variable)
Class Definition (stack.h) class Stack{ private: int size; // max size of storage int top; // index of the next available space int* data; // data storage public: Stack(int N); // a constructor ~Stack(); // a destructor // basic operations void push(int x); // add another datum int pop(); // get the most recent datum // status operations int num_elements() const; bool empty() const; bool full() const; }; Access Modifiers Data Members Member Functions Remember this semicolon.
Class components • Access modifiers • To control the access right of members inside the class. • Data member • Class member for holding data. • Member function (aka Function Member, Method) • Class member that perform actions on data members. • Constructor • First function to be called when an object is going to be created. Its name is the same as that of the class. • Destructor • Last function to be called when an object is destroying. Its name is the name of the class preceded by a ‘~’.
Object Oriented Programming Concepts
Why object oriented? • For example, const int MAX_ALTITUDE = 11000; const int MAX_SPEED = 960; struct Airplane{ int altitude; int speed; }; void takeoff(Airplane plane); void descend(Airplane plane, int feet);
Max possible speed 0 altitude Why object oriented? • There are some constraints on the Airplane class • Altitude of airplane must be >= 0 but less than some constants (MAX_ALTITUDE). • Not all speeds are possible at all altitudes.
Why object oriented? • Can’t guarantee every access considers these constraints • Altitude and speed are accessible throughout the entire program. • If not all members in a programming team follows the constraints strictly, then logical error will be resulted. • Difficult to debug if the values are found to be wrong. • You need to search over the entire program to fix the bugs.
Why object oriented? • How can constraints be enforced? • We can define a restricted set of functions that access the value of altitude and speed (of Airplane) to ensure data consistency. • Other parts of the program can access the value of altitude and speed only indirectly through this restricted set of functions. • We only have to ensure the constraints be considered in this restricted set of functions (instead of the entire program). • Easier to maintain the code, testing and debugging.
Why object oriented? • How can we ensure that the Airplane structure’s members are only accessed by a restricted set of functions? • In standard procedural programming languages (e.g. C), WE CAN’T ... • This inspired the development of Object-Oriented Programming (OOP) languages, • E.g., C++ and Java
OOP Features • Data abstraction • Encapsulation / Information Hiding • Inheritance (Reusability of code) • Polymorphism • We will discuss the first two features first.
Data Abstraction • A data abstraction is a simplified view of an object. • includes only features one is interested in while hides away the unnecessary details. E.g. • An airplane class is not as complicated as a real plane, it can only let you control the speed and direction of a plane. • In programming languages, a data abstraction becomes an abstract data type or a user-defined type. E.g. Fruit apple; • In OOP, it is implemented as a class.
Encapsulation / Information Hiding • An abstract specification tells us the behavior of an object independent of its implementation. • It tells us what an object does independent of how it works. • Information hiding is also known as data encapsulation, or representation independence. • The principle of information hiding: • Design a program so that the implementation of an object can be changed without affecting the rest of the program. • For example, you can create a stack using linked-list or array. That will not affect the stack’s public interface. • A stack should support “push” and “pop”
Classes and Programming • Conceptually a class is a blueprint or a design of something. • When we create a product according to this blueprint, that is an object. • E.g. to build five cars according to a Car blueprint • Although “object oriented” is a way to simulate our concepts in real-world, we can’t forget its “programming” part. • That means a class should have its own functions, data structures, data types, etc. • We have to learn a lot of syntax and special behavior of C++ in order to carry out those object oriented concepts.
Class Declaration and Class Definition • As in function, we can introduce the name of a class, without defining it • Class Definition • Define what a class contains • What is its data member and member functions • Including the type of data members and the declaration of member functions • Class Declaration • Introduce a class name only • Usage will be covered later • Class Implementation • Include all definition of the member functions
Header file (stack.h) class Stack; // class declaration class Stack{ // class definition private: int size; // max size of storage int top; // index of the next available space int* data; // data storage public: Stack(int N); // declaration of constructor ~Stack(); // declaration of a destructor void push(int x); // declaration of a member function int pop(); int num_elements() const; bool empty() const; bool full() const; };
Source file (stack.cpp) #include “stack.h” // remember to include the header file // class implementation (defines all member functions) Stack::Stack(int N) { size = N; top = 0; data = new int[size]; } Stack::~Stack(){ delete [] data; data = NULL; } void Stack::push(int x){ if ( full() ) cout << “ERROR: Stack is already full” << endl; else data[top++] = x; }
Source file (stack.cpp) […continue] int Stack::pop(){ if ( empty() ){ cout << “ERROR: Stack is empty” << endl; return -9999; } return data[--top]; } int Stack::num_elements() const{ return top; } bool Stack::empty() const{ return ( top == 0 ); } bool Stack::full() const{ return ( top == size ); }
Class Name: Uniqueness • The name of classes cannot be used in two definitions. class W { int a; }; // error, double class W { double b; }; // definition // OK, a class can be declared multiple times class R; // class declaration class R { int a; }; // class definition class R; // OK: another declaration
Class Name: Name Equivalence • A class definition introduces a new abstract data type. C++ relies on name equivalence (and NOT structure equivalence) for class types. class X { int a; }; // define two different classes class Y { int a; }; // with same structure X x; // create two objects for class X and Y Y y; x = y; // error: type mismatch; x and y are objects // of different types
Class Data Members • Data members can be of a pointer or reference of its own type: class A { A* pointerA; A& refA; A objA; // ERROR: cannot use its own type, except // pointer/reference }; • Using objA inside the definition of class A makes the size of object infinite, therefore it is not allowed. • However, pointer and reference must occupy 4 bytes only. It is allowed in the definition of class A.
Class Data Members - Forward Declaration • Data members of a class can be of any basic type, or any user-defined type if they are declared/defined: • A forward declaration for class pointers: class Cell; // forward declaration of Cell class class Stack { int size; Cell *top; // OK: because we declared that there is // a Cell class (forward declaration) Cell x; // Error: the definition of Cell class // is not complete! }; class Cell { int a; }; class Stack{ Cell x; }; // OK, Cell is already defined
Class Data Members - Necessity of Forward Declaration • Recursively uses user-defined type in class definition class A; // forward declaration class B{ // definition of class B A* pointerA; }; class A{ // definition of class A B* pointerB; B objB; // this is also allowed };
Class Data Members - Initialization • Data members can NOT be initialized (give initial values) inside the class definition. class Stack { // ... int top = 0; // Error: class data member // cannot be initialized here }; • Initialization should be done with appropriate constructors, or member functions. Stack::Stack(int N) { // constructor definition top = 0; // It is ok }
Class Member Functions • These are the functions declared inside the body of a class, but they can be defined in two ways: • Within the class body, they are inline functions class Stack { // ... void push(int x) { /* ... */ } int pop() { /* ... */ } }; • Outside the class body class Stack { // ... void push(int x); int pop(); }; void Stack::push(int x) { /* ... */ } int Stack::pop() { /* ... */ }
Constant Member Functions • ‘const’ keyword can guarantee that the member function won’t modify the invoking object. class Stack { … int num_elements() const { return size; } // OK int pop() const { --top; return top; } // ERROR }; • To define a function outside the class int Stack::num_elements() const { return size; }
Constant Member Functions • Also, for a constant object, it can call its constant member functions only. • Calling constructor and destructor are exceptions, because they can’t be declared as constant functions. Stack stk_a; const Stack stk_b; stk_a.num_elements(); // OK stk_b.num_elements(); // OK stk_a.pop(); // OK stk_b.pop(); // ERROR: constant object // accesses non-const member
Inline Functions • Member function can also be inlined class Stack{ // ... int num_elements() const { return top; } }; int main() { Stack stackA; cout << stackA.num_elements() << endl; return 0; } • The above cout statement is similar to cout << stackA.top << endl;
Inline Functions • On the other hand, when you define a member function inside the class, it is automatically treated as an inline function. class Stack { // ... // define the member function inline int num_elements() const { /* ... */ } int pop() { /* ... */ } };
Inline Functions • To enhance readability, you may also define inline functions in the same header file with the class but outside the class definition. // Stack.h class Stack { … inline void push(int x); // an inline function }; // put the inline function out of the class inline void Stack::push(int x) { top = x; ++top; }
Inline Functions • Recall that C++ compilers may NOT honor your inline declaration. • The inline declaration is just a hint to the compiler. • Compiler still has the freedom to choose whether to inline your function or not, especially when the function is large.
How are objects implemented? • Each class object gets its own copy of the data members. • All objects of the same class share one copy of the member functions. int main() { Stack x(2), y(3); x.push(1); y.push(2); y.pop(); }
Member Access Control • A member (data or function) of a class can be: • public : accessible to anybody (inside or outside the class). • private : accessible only to member functions (include another object of the same class). It is mainly used to enforce information hiding. class A { private: int x; public: A() { x = 3; } // initialize x to 3 void add(A another) { x = another.x + x; } // it is ok to access another object of the // same class. Not prohibited by private. }; • protected: it will be discussed later.
Accessing Member Functions / Data Members • Use member access operator (.) Stack stackA(12); stackA.push(3); // call a member function // in Stack class • Use dereference member access operator (->) // a pointer to a Stack object Stack* stackA = new Stack(12); stackA->push(3); • It is equal to a deference operator plus a member access operator, i.e. (*stackA).push(3); • You can also use these operators to access the data members
“this” Pointer • Each class member function implicitly contains a pointer of its class type named “this”. • When an object calls the function, this pointer is set to point to the invoking object. • For example, the C++ compiler developed by AT&T Bell Labs will automatically translate the function void Stack::push(int x) in the class Stack to a unique global function by adding a new argument: void Stack::push(Stack* this, int x) { if ( this->full() ) cout << “ERROR: Stack is already full” << endl; else this->data[ (this->top)++ ] = x; } • a.push(x) becomes push(&a, x).
“this” Pointer of constant member function • Since a constant member function cannot modify the data members, the “this” pointer is declaraed as a constant pointer type void Stack::num_elements(const Stack* this) { return top; // return this->top; // equivalent }
Necessity of using “this” Pointer • Return an object by “this”. class Complex { private: float real; float imag; public: // Addition of complex numbers Complex add( const Complex& x) { real += x.real; imag += x.imag; return *this; // return the object itself } }; • Use the above code, we can do something like this Complex x, y, z; // ... Complex a = z.add( x.add(y) );
Class Scope and Scope Operator :: • C++ statically binds name occurrences to declarations at compile time int height; // global “height” class Hill { int height; // local “height” Hill() { height = 0; } // constructor }; • Which “height” does Hill() using? • Hill::height (the one defined in the class Hill) • How to access the global “height” inside class Hill? • ::height
Class Scope and Scope Operator :: #include <iostream> using namespace std; int height; class Hill { private: int height; public: Hill() { // constructor ::height = 0; // Access global “height” using :: height = 7; // Access local “height” } void print() {cout << "local height " << height << endl; } }; void main() { Hill h; h.print(); cout << "global height " << height << endl; }
Constant Data Member • Sometimes, a data member should not be changed after the object has been constructed. class Airplane { private: const string MODEL_NUMBER; int altitude; int speed; public: Airplane(string modelNumber, int altitude, int speed); void takeoff(Airplane plane); void descend(Airplane plane, int feet); void print() const; };