Transcript OOP
Object Oriented
Programming
Lecture #8
Elhanan Borenstein
[email protected]
copyrights © Elhanan Borenstein
Agenda
Inheritance & Polymorphism – A short reminder
File Handling in C++
Polymorphism & Files: 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(){…}
};
copyrights © Elhanan Borenstein
Inheritance
The Logic behind Inheritance
When inheriting a class, we inherit all
its members and they will be included
in the derived class: Base (father)
Additions
Somewhat similar to object data
members (embedded):
Derived (son) (SIZE?)
inner object
Additions
embedding
class
To differentiate, we can check the logics behind the
implemented entities:
Inheritance: The derived class is a type of the base class.
Inner class: The inner class is a part of the external class.
copyrights © Elhanan Borenstein
Inheritance
Understanding Inheritance - Example
class GraphicItem
{
int color;
public:
GraphicItem() {…} // constructor
void Draw() {…}
void ChangeColor(int n_clr) {…}
};
…
class CPoint : public GraphicItem
{
int m_x, m_y;
public:
CPoint() {…} // constructor
void Draw() {…} // override
void Align(){…}
};
P = G;
GraphicItem G, *pG;
CPoint
P, *pP;
…
G = P;
pG = pP;
pP = pG;
…
copyrights © Elhanan Borenstein
Inheritance
Accessing the Base Class Data Members
All the data members of the base class also exists in the
derived class. But there are cases where we cannot access
these members.
Hiding (but we can still use the full name (base_class::member)
Private Permission (but we can still use public methods for access)
Using the Base Class Member Functions
The derived class also inherit all the base class methods.
The derived class can override inherited methods
We can still activate the base method using its full name.
C’tor, D’tor and C.C. are the only methods that have a
different behavior when inherited.
copyrights © Elhanan Borenstein
Polymorphism
Introduction
The Polymorphism mechanism allows for an action to
performs differently according to the object type, while
being transparent to the programmer.
As we recall, a pointer to the base class can also point to
objects from a derived class.
There will be no data loss
We will be able to activate methods on that object, only if they exist
in the base class.
The issue is however, which method will be activated
(base or derive)?
copyrights © Elhanan Borenstein
Polymorphism
Example
class Employee
{
double Salary;
public:
void Print() const {…}
void RaiseSalary (double r)
{
Salary += r;
Print();
}
};
main ()
{
class Manager : public Employee
{
…
public:
void Print() {…}
};
}
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.
Usage: Containers, Generic Algorithms
copyrights © Elhanan Borenstein
Polymorphism
Abstract Classes
There are cases (especially when we implemented a
General Container), where there is no intention to actually
create an object of the base class.
In such a case, we wish to:
Avoid implementation
Prevent the programmer from creating objects of the base class.
The solution is Pure Functions and Abstract Classes:
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.
copyrights © Elhanan Borenstein
Polymorphism
How Does Dynamic Binding Work?
Dynamic binding is implemented by a virtual functions table.
Each object that includes a virtual function will also include
a pointer to this table (usually two bytes pointer):
Calling a virtual function will thus require two memory calls:
class A
{
int a;
public:
virtual void f() {…}
virtual void h() {…}
virtual void g() {…}
};
class B : public A
{
int b;
public:
virtual void g() {…}
};
class C : public B
{
int c;
public:
virtual void h() {…}
};
C vftable
C obj;
a
vfptr
b
c
A::f()
B::g()
C::h()
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.)
copyrights © Elhanan Borenstein
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:
C file handling functions : “global” functions that get a parameter of
type FILE*
C++ file handling functions : methods of a class which represents a
file
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?
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).
When ever we include <iostream>, an ostream object, pointing to
stdout is automatically defined – this object is cout.
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 c’tor (the natural and preferred way).
void ofstream / ifstream::open(const char* filename, int mode, int prot);
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 / doesn’t exist
ios::trunc – open an empty file
ios::binary – open a binary file (default is textual)
prot – file type (read only, hidden, etc.)
Don’t 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).
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).
copyrights © Elhanan Borenstein
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
To read:
#include <fstream.h>
main()
{
// Writing to file
ofstream OutFile("my_file.txt");
OutFile<<"Hello "<<5<<endl;
OutFile.close();
get() – reading a single
character of a buffer
int number;
char dummy[15];
getline() – reading a single line
>> operator – reading a object
// Reading from file
ifstream InFile("my_file.txt");
InFile>>dummy>>number;
InFile.seekg(0);
InFile.getline(dummy, sizeof(dummy));
InFile.close();
}
copyrights © Elhanan Borenstein
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
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
Each class we wish to be able to store, 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 C’tor 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.
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
Questions?
copyrights © Elhanan Borenstein