Sunteți pe pagina 1din 28

Object Oriented Programming

Lecture #8 Elhanan Borenstein borens@tau.ac.il

copyrights Elhanan Borenstein

Agenda
Inheritance & Polymorphism A short reminder File Handling in C++ Polymorphism, Files and Serialization

copyrights Elhanan Borenstein

Inheritance & Polymorphism a Short Reminder

copyrights Elhanan Borenstein

Inheritance
Example
class Base1 { int var1; public: Base1() {} // constructor void OldFunc() {} void Init() {} }; class Derived1 : [public|protected|private] Base1 { int var2; public: Derived1() {} // constructor void Init() {} // override void Do(){} };

Base1 b();
var1

Derived1 d();
var1

var2

d.Init(); d.OldFunc(); d.Do();


copyrights Elhanan Borenstein

Polymorphism
Introduction
As we recall, a pointer to the base class can also point to objects from a derived class. There will be no data loss e.g.: CGraphicalItem* items[7];

CPoint CRect

CPoint

CLine CCircle

copyrights Elhanan Borenstein

Polymorphism
Example
class Employee { double Salary; public: void Print() const {} void RaiseSalary (double r) { Salary += r; Print(); } }; class Manager : public Employee { public: void Print() {} };

main () { Employee *pE; pE = new Manager; pE->Print(); Manager m; m.Print(); m.RaiseSalary(); }

copyrights Elhanan Borenstein

Polymorphism
Virtual Methods and Dynamic Binding
When activating a method through a pointer to the base class: if the method was not defined as virtual:
A Static Binding is performed (on compilation) The base class method will be activated

If the method was define as virtual:


A Dynamic Binding will be performed (on runtime) The method of the appropriate class will be activated.

Our goal in polymorphism is to avoid checking the actual type of an object by using virtual methods
copyrights Elhanan Borenstein

Polymorphism
General Containers
General Containers support the implementation of data structures using pointers to Data* while actually storing objects that are derived from Data. Examples: Humus, MFC objects (CObArray), Graphic objects

Generic Algorithms
Generic Algorithms form a skeleton of the algorithms by calling methods which will be implemented differently by each derived class. Example: Print, Draw Object, Calc3DVolume.

copyrights Elhanan Borenstein

Polymorphism
Abstract Classes
There are cases (especially when we implement a General Container), where there is no intention to actually create an object of the base class. In such a case, we wish to:
1. 2.

Avoid implementation of some of the base class virtual methods (which will be overridden by the derived classes anyway). Prevent the programmer from creating objects of the base class. A pure function is a virtual function, declared in the base class only for the purpose of implementing it in the derived classes. To define a function as pure we will add =0 in the prototype. A class with one or more pure function is automatically an abstract class objects of that class cannot be created (or passed ByVal).

The solution is Pure Functions and Abstract Classes:

Example: abstract

copyrights Elhanan Borenstein

Polymorphism
Try that
class A { public: virtual void f() {cout<<A;} void h() {cout<<a;} virtual void g() { f(); h(); } }; class B : public A { public: virtual void f() {cout<<B;} void h() {cout<<b;} }; main() { B* p; p = new C; p->g(); p->h(); }; class C : public B { public: virtual void f() {cout<<C;} void h() {cout<<c;} };

1. 2. 3. 4. 5.

Abc Cab Acb Cac Ccc


copyrights Elhanan Borenstein

Polymorphism
Runtime Type Information
There are scenarios where we are required to check what is the real object type that we actually hold. (WHEN?) We can implement our own mechanism using a virtual function called Type(). Alternatively, the operator typeid can be used.
#include <typeinfo.h> is required (not an integral part of C++) Setting the project to work with RTTI is required The return value is an object of type typeinfo, that supports the method name() and the operator == typeid also works on fundamental data types (int, float, etc.)

Example: typeinfo
copyrights Elhanan Borenstein

Polymorphism
Dynamic Casting
Dynamic casting is a safe method to perform casting to a specific derived class while checking if it actually points to an object of that class. There are cases where dynamic_cast has an advantage over typeid. For example:
class A { }; class B : public A { void Do() {} }; class C ; public B { }; }
copyrights Elhanan Borenstein

void main() { A* pa = new C; if (typeid(*pa)==typeid(B)) ((B*)pa)->Do(); B* pb=dynamic_cast<B*>(pa); if (pb) pb->Do();

File Handling in C++

copyrights Elhanan Borenstein

File Handling Classes


Working with Files
C++ includes special classes for file handling (reading from files / writing to files). We already had a short example where these classes were used (when overloading the operator >>:
ostream writing to the screen, ofstream writing to a file (C file handling functions (fopen, fprintf, fread, fwrite) can still be used).

NOTE: File handling functions


C: global functions that get a parameter of type FILE* C++: methods of a class which represents a file

C:
FILE* pfile; fwrite(pfile, array, size, );

C++:
ofstream out_file(); out_file.write(array, size, );
copyrights Elhanan Borenstein

File Handling Classes


General
When working with files in C++, the following classes can be used:
ofstream writing to a file ifstream reading for a file fstream reading/writing (multiple inheritance) and many other derived and base classes

What does it all have to do with cout?


When ever we include <iostream>, an ostream object, pointing to stdout is automatically defined this object is cout.

ofstream inherits from the class ostream (standard output class). ostream overloaded the operator >> for standard output.thus an ofstream object can use methods and operators defined in ostream (as we seen in the example).

copyrights Elhanan Borenstein

File Handling Classes


Hierarchy Diagram

copyrights Elhanan Borenstein

File Handling Classes


Opening a File
A file can be open by the method open() or immediately in the ctor (the natural and preferred way).
void ofstream / ifstream::open(const char* filename, int mode); filename file to open (full path or local) mode how to open (one or more of the following using | )
ios::app append ios::ate open with marker at the end of the file ios::in / ios::out (the defaults of ifstream and ofstream) ios:nocreate / ios::noreplace open only if the file exists / doesnt exist ios::trunc open an empty file ios::binary open a binary file (default is textual)

Dont forget to close the file using the method close()


copyrights Elhanan Borenstein

File Handling Classes


Querying a File
is_open() Checking whether the file was open correctly. (for compatibility with C, the operator ! was overloaded).
FILE* pfile = fopen(); if (!pfile) ofstream out_file(); if (!out_file)

rd_state() returns a variable with one or more (check with AND) of the following options:
ios::goodbit OK ios::eofbit marker on EOF ios::failbit illegal action, but alright to continue ios:badbit corrupted file, cannot be used.

We can also access the bit we wish to check with eof(), good(), fail(), bad().
clear() is used to clear the status bits (after they were checked). Borenstein copyrights Elhanan

File Handling Classes


Moving within the File
seekg() / seekp() moving the reading (get) / writing (put) marker
two parameters: offset and anchor

tellg() / tellp() getting the position of the reading (get) / writing (put) marker

copyrights Elhanan Borenstein

File Handling Classes


Reading /Writing from/to Textual Files
To write:
put() writing single character << operator writing an object
#include <fstream.h> main() { // Writing to file ofstream OutFile("my_file.txt"); OutFile<<"Hello "<<5<<endl; OutFile.close(); int number; char dummy[15]; // Reading from file ifstream InFile("my_file.txt"); InFile>>dummy>>number; InFile.seekg(0); InFile.getline(dummy, sizeof(dummy)); InFile.close(); }
copyrights Elhanan Borenstein

To read:
get() reading a single character of a buffer getline() reading a single line >> operator reading a object

File Handling Classes


Reading /Writing from/to Binary Files
To write n bytes:
write (const unsigned char* buffer, int n);

To read n bytes (to a pre-allocated buffer):


read (unsighed char* buffer, int num) Use: int gcount() to check how many byte where actually read (WHY) Note: Unlike C, the buffers are of type unsigned char* (and not void*)
#include <fstream.h> main() { int array[] = {10,23,3,7,9,11,253}; ofstream OutBinaryFile("my_b_file.txt, ios::out | ios::binary); OutBinaryFile.write((char*) array, sizeof(array)); OutBinaryFile.close(); }
copyrights Elhanan Borenstein

Polymorphism, Files
and

Serialization

copyrights Elhanan Borenstein

Serialization
Introduction
Serialization is a common method to store (save) and retrieve (load) data objects. It can be used to save database records, the status of the application, etc. In C:
we could store all the required fields in structures and then use fread/fwrite to store this data in a binary file. Important pointers should not be stored (as they will be invalid when we load them. Instead, whenever a dynamic allocation was used, the file should store the size of the allocation and then the content (and load appropriately).

In C++: the same mechanism is use but naturally using Classes, Polymorphism and Object Oriented paradigm.

copyrights Elhanan Borenstein

copyrights Elhanan Borenstein

Serialization in C++
Saving & Loading Objects
For each class we wish to be able to store, we will have a Save() method (this method will handle saving the object data members to the file).
Save() will get as parameter an object of type ofstream. If the object includes dynamic allocations, it should not store the pointer, but rather the size of the allocated memory and its content. Note: if the class includes (or inherits) virtual functions, the pointer to the vf table should not be saved. A derived class can (and should) use the base class Save method (e.g. base_name::Save()) to store the base data members and only then save the new derived members.

Each such class will also have:


A Load Ctor that gets as a parameters an object of type ifstream and builds a new object according to the data in the file A Load() method that can read the data to an existing object. When loading a dyn. allocated member, a new allocation is made.

copyrights Elhanan Borenstein

Serialization in C++
21,000 ptr 20

h e l

l o \n

21,000
4 Bytes

5
4 Bytes

h e l
5 Bytes

l o

20
4 Bytes

Serialization in C++
Using Containers
When using a general container (which holds objects of various classes with a common base class)
we wish to be able to use the Save() method transparently. Thus, we will define the Save() method in the base class as virtual. The container should also implement a Save() method which will traverse all the objects it holds and call their Save() method.

What about the container Load() method?


The container has to know which objects should be created. It is thus common to store in the file, before each object a code (textual, binary, etc.) which will identify the object type. The container Load() method, will first read this code and then create the appropriate object. Branching should be performed wisely (switch, hash table, etc.)
copyrights Elhanan Borenstein

Serialization in C++
Example
We wish to implement a data base which will allow the user to input / print / save / load the list of employees in the company. For each employee in the company we wish to store:
Name (char*), Salary (float)

Some of the employees are managers. Manager record should also include:
Level (int)

Obviously, we wish to have as little as possible locations where we distinct between employees and managers. Example: Human Resources Data Base
copyrights Elhanan Borenstein

hr_db
2 ptr ptr ptr

ptr 23.5

B e n \n

ptr 410.5 10

B i

l \n

e m

B e n 23.5 m a

B i

l l 410.5 10

Questions?
copyrights Elhanan Borenstein

S-ar putea să vă placă și