More on Object Oriented Programming Concepts Functional, structured programming often results in programs that describe a hierarchy of tasks to be performed. Object oriented design, however, results in a hierarchy of objects. Concepts: An object is a class object or a class instance. A method is public member function An instance variable is a private data member Sending a message means calling a public member function There are three basic elements in object oriented programming: encapsulation, inheritance and polymorphism. Inheritance: This is a mechanism whereby a hierarchy of classes is constructed such that each descendant class inherits the properties (data and operations) of its ancestor class. A child class automatically inherits all properties of the parent class and can in addition have more specialized properties. The inheritance relationship can be viewed as an is-a relationship with objects becoming more specialized as we go lower in the hierarchy. Object-oriented languages provide a way for creating inheritance relationships among classes. You can take an existing class (called base class) and create a new class from it (called derived class). The derived
class inherits all the properties of its base class, in particular the data and operations defined for the base class. Polymorphism: Polymorphism means various forms. In OOP it is the ability to determine either statically or dynamically which of the several methods with the same name (within the class hierarchy) should be invoked. Overloading means giving the same name to several different functions. The compiler selects the correct method based on the parameter types, values, etc The time at which a function name is associated with the code is called binding time. Binding time can be static if the binding occurs at compile-time, or dynamic if the decision is postponed until run time. C++ constructs for OOP In an object-oriented design of a program, classes typically exhibit one of the following relationships: They are independent of one another, they are related by composition, or they are related by inheritance. Composition: Or containment is a relationship in which a class contains a data member that is an object of another class type. For example, we can define a class PersonType that has a data member birthdate of class DateType #include <string> class PersonType { public:
}; void Initialize(string, DateType); string GetName() const; DateType GetBirthdate() const; private: string name; DateType birthdate; Deriving One Class from Another #include <string> class MoneyType { public: void Initialize(long, long); long GetDollars() const; long GetCents() const; private: long dollars; long cents; }; class ExtMoneyType: public MoneyType { public: string GetCurrency() const; void Initialize(long, long, string); private: string currency; }; ExtMoneyType extmoney; void ExtMoneyType::Initialize (long newdollars, long newcents, string newcurrency)
{ } currency=newcurrency; MoneyType::Initialize(newDollars, newcents); string ExtMoneyType::GetCurrency() const { return currency; } Note: the scope resolution operator (::) appears between the type MoneyType and the member function Initialize in the definition of the member function, because two member functions named Initialize now exist. And we must indicate the class in which one we mean by putting the class type. If we fail to do so explicitly, the computer assumes that we mean the most recently defined. In C++ rule for passing parameters is that the actual parameter and its corresponding formal parameter must be of an identical datatype. With inheritance, however, C++ relaxes the rule, that is the type of the actual parameter must be an object of a derived class of the formal parameter. examples: money. Initialize(20, 66); extmoney.initialize(20, 66, francs ); Constructs for program verification: General Methods for verifying software correctness: The first characteristic of a good program is that it accomplishes its intended function. We can only know that by testing the program.
Testing: The process of executing a program with data sets designed to discover errors. If the program execution does not yield the expected results, we need to debug the program Debugging: the process of removing known errors. When debugging is completed, the software is put into use. Usually it is installed on one or more customer sites so that it can be tested in a real environment with real data. This is the acceptance test phase. After passing the test, the program becomes operational. The maintenance phase of the software include correction of errors, addition of new capabilities. Testing must occur after any program verification, this is called regression testing. Testing is useful for in revealing the presence of bugs in the program, however it does not prove that a program has none. Origin of bugs: Specifications and Design Errors Specification are what the customer wants for your software to perform. Most studies show that it costs 100 times more to correct an error discovered after the software delivery than it does if the problem is discovered early in the software life cycle. Compile time errors: Run time errors: Errors that occur during execution of a program and are usually more difficult to detect than syntax errors.
Designing Correct Programs: The verification of program correctness, independent of data testing, is an important area of theoretical computer science research. The research seeks to establish a method for proving programs that is analogous to the method for proving theorems in geometry. Assertion: An assertion is a logical proposition that can be true or false. We can make assertions about the state of a program For example: sum= part +1 ; we might assert the following the value of sum is greater than the value of part The general concept behind program verification is that we can make assertions about what the program is intended to do, based on its specifications and then prove through a logical argument rather than through the execution of the program that the design or implementation satisfies the assertions. The process is broken down into 2 steps: 1. Correctly assert the intended function of the part of the program to be verified 2. Prove that the actual design or implementation does what is asserted. Preconditions and Postconditions: Imagine you want to design a module or a logical chunk of the program to perform some specific
operation. To ensure that this module (usually implemented as a function) fits into the program as a whole, we must clarify what happens at its boundaries - that is what must be true when we enter the module and what must be true when we exit. We call the assertions that must be true on entry into the function preconditions. Preconditions: Assertions that must be true on entry into an operation or function for the postcondition to be guaranteed. In addition, we must also know what conditions are true when the operation is complete. The postconditions are assertions that describe the results of the operation Postconditions: Assertions that state what results are expected at the exit of an operation or function, assuming that the preconditions are true. Example: Let consider the preconditions and postconditions for an operation that removes the last element of a list and returns its value as an output. The specification for GetLast is: GetLast( ListType list, ValueType lastvalue) Function: Remove the last element in the list and return its value in lastvalue. Precondition: the list is not empty Postcondition: lastvalue is the value of the last element in the list, the last element has been removed, and the list length has been decremented Exceptions:
In the design phase, we should plan how to handle exceptions in our program. Exceptions are exceptional situations. When these situations occur, the flow of control of the program must be altered, usually resulting in the premature end to program execution. Some languages (such as C++ and java) provide built-in mechanisms to manage exceptions. All exceptions mechanisms have three parts: -Defining the exception (try) - Generating (raising) the exception (throw) - Handling the exception (catch) Example: A bounded counter is a software device that maintains and updates a counter value. When the counter increments past a certain limiting value, set when the counter is first created, it wraps around back to zero. Suppose we have a counter that is created with a limiting value of 17. A series of increments might take us through the values 14, 15, 16, 17 and then back to 0. Define an ADT that will implement a bounded counter. Give methods for increment, decrement and accessing the current counter value.