530 likes | 1.36k Views
Data Abstraction and Object-Oriented Programming. CS351 – Programming Paradigms. Object Oriented Programming. The development of complicated computer applications has seen the need for data abstraction with the software engineering field. The main advantages of data abstraction are:
E N D
Data Abstraction and Object-Oriented Programming CS351 – Programming Paradigms
Object Oriented Programming • The development of complicated computer applications has seen the need for data abstraction with the software engineering field. • The main advantages of data abstraction are: • It reduces the conceptual load for the programmer. • It provides a method of fault containment by preventing the programmer from using code in an inappropriate way. • It increases the independence among program components. Programmers can work on individual parts of the code without affecting the interface between components. • The final point is difficult to achieve in the ``wild’’
Defining a class – C++ Style class list_node { private: list_node* prev; list_node* next; list_node* head; public: int val; list_node(); ~list_node(); list_node* get_prev(); list_node* get_next(); void insert(); void remove(); }; Access Specifiers Destructor ; after the }
Defining a class – C++ Style class list { public: int empty(); list_node* head(); void append(list_node* ln); ~list(); }; • In C++, the boolean type can be represented by either the bool type which works identically to the Java version or through the use of an integer value ( carried over from C). • Any non-zero integer value evaluates to true in C++. Booleans are ints in C++!
Defining a class - cont • This class definition is usually contained within a ``header’’ file, typically labelled file.h • To create instances (objects) of the list_node class: • list_node element; • list_node * element = new list_node(); • Both are equally valid ways of creating the object in C++, what is the difference between the two? • The invocation of the special method to create an object is called the constructor. • In programming languages that do not feature automatic memory management, the objects must be deleted manually via a special method known as the destructor.
Data Access • OOP allows data to be hidden and protected in a way that is different to imperative programming. • We can declare data that belongs to a class as either public or private. • This allows us to control the visibility of the data when classes interact. • In C++, every member is private by default, but the use of the public: syntax allows any fields that follow to be declared as public. • This is in contrast to the simple nature of the struct in C. All of the data members in structs are public in C but private in C++!!
Subroutines • These are referred to as methods in OOP. • As data members can be declared to be private, it is convention to supply get and set methods for each private member. • Our code for list_node should now show the following changes: private: … int val; public: void set_val ( int v ); int get_val (); …
Derived Classes • In OOP we can choose to have an abstract idea coded in a base class. • We can then derive a concrete class from the base class. • This derivation allows the derived class (child class, sub class) to inherit all of the features of the parent class. • By deriving new classes from old ones, the programmer can create arbitrarily deep class hierarchies with added functionality at every level of the hierarchy. • Our code from earlier now looks like this: class queue : public list { public: void enqueue (list_node * n); list_node * dequeue(); }; Inheritence in C++
Accessing Derived Methods • In the base class, the method definiton for remove could look like this: void list::remove() { head = head->next; } • In the derived class, queue, how do we call methods from the parent class? • In Java we can use the super keyword to call methods and fields from the parent class. • To call methods belonging to a class in C++, we use the scope resolution operator :: void queue::remove() { list::remove(); }
Encapsulation & Inheritance • Encapsulation is a technique that allows the programmer to group data and the subroutines that operate on that data and place them together in a single place. • With the ability to inherit fields and methods from base classes, some rules for data hiding and data management must be defined. • Some questions to be answered include: • Should private members of a base class be visible to methods of a derived class? • Should public members of a base class always be public members of a derived class? • How much control should a base class have over the visibility of its members?
Example class list { public: int empty(); list_node* head(); void append(list_node* ln); ~list(); }; class queue : private list { public: using list::empty; using list::head; void enqueue (list_node * n); list_node * dequeue(); }; We are extending the class list privately. What difference does this make? We must explicitly make public methods that are privately derived accessible. We use the using keyword for this purpose.
C++ Visibility Rules • Any class can limit the visibility of its members. • Public members are visible anywhere the class declaration is in scope. • Private members are visible only inside the classes methods. • Protected members are visible only inside the methods of the class or any of its descendants. • An exception, the friend keyword, what does that do? • A derived class can restrict the visibility of members of a base class but can never increase it. • Private members of a base class are never visible in a child class. • Public and protected members of a base class are public or protected respectively in a derived class. • Public and protected members of a privately derived base class are private members of the derived class. • A protected or privately derived base class can have its members visibility restored through an explicit using declaration.
Comparison versus Java • Java also features the keywords public, private and protected. • There are slight semantic differences In Java though: • Classes cannot be derived as protected or private, hence a derived class cannot increase or restrict the visibility of members of the base class. • The protected keyword has a different meaning In Java also. A protected member is visible not only in the base class and derived classes but also throughout the whole package it is declared in.
Constructors and Destructors • Most object-oriented languages provide some features for initialising an object at the start of its lifetime. • This special method is known as a constructor. • This constructor initializes the space in memory that has been allocated. • How does the compiler know to allocate the space? • Some other languages provide methods, known as a destructor, to provide some housekeeping after the object has reached the end of its lifetime. • We have seen that the tilda, ~, is used in C++ to indicate that the special method is a destructor. • How do you create a destructor in Java?
Execution Order for Constructors • When an object of a derived class is created in C++, the compiler guarantees that the constructor for any base classes will be executed, outermost first, before the constructor of the derived class. • How do we manage this in C++, given the absence of a super keyword? class queue : public list { … public : queue::queue( args passed to queue ) : list ( args for list ) { … } … }; • Similar syntax can also be used to pass default values to a constructor. list_node::list_node( ) : val ( 0 ) { } This is the same as in Java: queue ( args ) { super ( list args ); }
Dynamic Method Binding • One of the main advantages of inheritance is that some derived class D has all of the members of its base class B. • Once D is not hiding any of the public members of B, then it is possible for an object of D to represent B in any context where a B could be used. • This feature is known as subtype polymorphism.
An Example of Dynamic Binding class person { … void person :: print() { // prints details for the person } }; class student : public person { … void student :: print () { //adds more specific information } }; class lecturer : public person { … void lecturer :: print () { //adds more details } }; … student *s = new student (); lecturer *t = new lecturer (); s -> print (); t -> print (); person *x = s; person *y = t; x -> print () ; y -> print () ; This is a polymorphic call
Example cont… person *x = s; person *y = t; x -> print () ; y -> print () ; • Does the choice of the method to be called depend on the types of x and y ? • If so then this is known as static binding. • Does the choice of the method to be called depend on the classes of the objects s and t to which those variables refer ? • If so then is known as dynamic binding. • Dynamic binding is central to OOP and ensures that even if we are using the base class to refer to a child class, the correct methods will always be called.