before introducing inheritance … here is another way to re-use what have been developed: this is...

40
Before Introducing Inheritance … re is another way to re-use what have been developed: This is known as the object composition! A real-world example of composition may be seen in the relation of an automobile to its parts.

Upload: dennis-francis

Post on 18-Dec-2015

213 views

Category:

Documents


0 download

TRANSCRIPT

Before Introducing Inheritance …

Here is another way to re-use what have been developed:

This is known as the object composition!

A real-world example of composition may be seen in the relation of an automobile to its parts.

How to generate complex object type from a number of basic types?

This is known as the object composition!

class Point{private: float x, y;};

class Edge{private: Point vertex1, vertex2};

class Triangle{public: private: Point vertex1, vertex2, vertex3; Edge edge1, edge2, edge3;};

How to initialize the composite objects?

This is known as the object composition!

class Point{private: float x, y;};

class Edge{public: Edge (const Point &p1, const Point &p2)

: vertex1(p1), vertex2(p2) {…}

private: Point vertex1, vertex2;};

class Triangle{……};

What if the composite class is defined as follows?

class Point{private: float x, y;};

class Edge{private: Point vertex1, vertex2;};

class Polygon{public: private: Point *vertices; Edge *edges; int nvertices, nedges;};

Inheritance and Derived Classes

Why do we need it?

– It is one of the most powerful mechanisms offered by OOP.

– It enables potentially infinite reuse of resources.– It can be used to enhance and improve the

previous programs for the changing requirements.– It also matches what we know about the real-

world.

An Example (from textbook)

Consider the employees of a company.

All of them have some common attributes, like SSN, name, employeeID, netPay, etc.

But they may be paid differently. Some are paid hourly, while the others may be paid with a fixed wage (weekly/monthly)

If we use two different classes to represent these two groups of employees who are paid differently, each of these classes will need to define those common attributes listed above.

This is not very efficient in terms of coding.

This situation can be more dramatic in the real cases of software development.

An Example (from textbook)

Consider the employees of a company.

All of them have some common attributes, like SSN, name, employeeID, netPay, etc.

But they may be paid differently. Some are paid hourly, while the other may be paid with a fixed wage (weekly/monthly)

To address that, we use the inheritance!

An Example (from textbook)

#include <string>using std::string;

class Employee{public: // constructor list // member functionsprivate: string name; string ssn; double netPay;};

We first define a class to implement the common properties and behaviors of all employees.

An Example (from textbook)

#include <string>using std::string;

class Employee{public: // constructor list // member functionsprivate: string name; string ssn; double netPay;};

Next, we define a new class to *inherit* these common attributes and behaviors.

class HourlyEmployee : public Employee{public: // constructor list // new member functionsprivate: double wageRate; double hours;};

An Example (from textbook)

#include <string>using std::string;

class Employee{public: // constructor list // member functionsprivate: string name; string ssn; double netPay;};

Next, we define a new class to *inherit* these common attributes and behaviors.

Base class or parent class

derived class or child class

class HourlyEmployee : public Employee{public: // constructor list // new member functionsprivate: double wageRate; double hours;};

In other words, we create a new class *based on* an existing class

How to use inheritance

From this example, we see the inheritance is achieved in the following format

class DerivedClass : public BaseClass{};

From this example, we can also learn that the base class typically provides a *general* definition (for all objects), while the individual child classes provide more *specific* definition (sub-sets of the objects).

How to use inheritance

class DerivedClass : public BaseClass{};

This inheritance can be carried on during the development of the program, and eventually we obtain an inheritance relation of the classes much similar to a family tree.

A

B C

D

E……

To represent this inheritance relation between classes and other relations including friend relationship, we will use UML!

What benefits can inheritance provide?

Derived class *inherits* all the member variables and functions except the constructors of the base class.

So what do you think is the benefit here?

Derived class can define its own and new members that are for its specific purpose! A B

More on Inheritance

Derived class can *overwrite (or re-define)* the inherited member functions from the base class. This will NOT affect the previous definition in the base class.

Derived class can NOT change the types of the inherited member variables.

Again, derived class does NOT inherit the private members of the base class and its constructors.

class DerivedClass : public BaseClass {}

class DerivedClass : private BaseClass {}

We typically use public access to inherit base class. “private” or “protected” inheritance is possible, but seldom used.

All members of the base class are now private to the derived class!

CORRECTION!

Derived class *inherits* all the public member variables and functions except the constructors of the base class.

Derived class DOES NOT inherits all the private member variables and functions of the base class.

So what do you think is the benefit here?

Derived class can define its own and new members that are for its specific purpose!

Correction: the memory allocated for the derived object contains everything from the base class including its private members! But you cannot directly access that!

A B

CORRECTION!

Derived class *inherits* all the member variables and functions except the constructors and destructor of the base class.

So what do you think is the benefit here?

Derived class can define its own and new members that are for its specific purpose!

Correction: the memory allocated for the derived object contains everything from the base class including its private members! But you cannot directly access that!

A B

Derived class "inherits" private member variables- But still cannot directly access them, not even in the member function of the derived class.

class my_emp{public:

my_emp(){name = NULL;age = 0;salary = 0.0;}

my_emp(char *in_name, int in_age, float in_salary):salary(in_salary), age(in_age){name = new char[strlen(in_name)+1];strcpy(name, in_name);}

~my_emp(){ delete [] name; }

void print_name() const{ cout << "This is " << name << endl;}

int get_age () const{ return age; }

float get_salary() const{ return salary; }

private:char *name;int age;float salary;

};

class manager : public my_emp{public:

manager(char *in_name, short in_age, float in_salary, int in_level): my_emp(in_name, in_age, in_salary) { level=in_level; }

void print_level(){ cout << "level: " << level << endl; cout<<“age: "<<age<<endl; //ILLEGAL!}

private:int level;

};

void main(){

manager m ("Chen", 0, 0, 1);m.age = 25; //ILLEGAL!m.salary = 1000000; //ILLEGAL!m.print_level();m.print_name();

}

class my_emp{public:

my_emp(){name = NULL;age = 0;salary = 0.0;}

my_emp(char *in_name, int in_age, float in_salary):salary(in_salary), age(in_age){name = new char[strlen(in_name)+1];strcpy(name, in_name);}

~my_emp(){ delete [] name; }

void print_name() const{ cout << "This is " << name << endl;}

int get_age () const{ return age; }

float get_salary() const{ return salary; }

private:char *name;int age;float salary;

};

class manager : public my_emp{public:

manager(char *in_name, short in_age, float in_salary, int in_level): my_emp(in_name, in_age, in_salary) { level=in_level; }

void print_level(){ cout << "level: " << level << endl; }

private:int level;

};

void main(){

manager m ("Chen", 0, 0, 1);m.print_level();m.print_name();

}

What is the output?

class my_emp{public:

my_emp(){name = NULL;age = 0;salary = 0.0;}

my_emp(char *in_name, int in_age, float in_salary):salary(in_salary), age(in_age){name = new char[strlen(in_name)+1];strcpy(name, in_name);}

~my_emp(){ delete [] name; }

void print_name() const{ cout << "This is " << name << endl;}

int get_age () const{ return age; }

float get_salary() const{ return salary; }

private:char *name;int age;float salary;

};

class manager : public my_emp{public:

manager(char *in_name, short in_age, float in_salary, int in_level): my_emp(in_name, in_age, in_salary) { level=in_level; }

void print_level(){ cout << "level: " << level << endl; }

private:int level;

};

void main(){

manager m ("Chen", 0, 0, 1);m.print_level();m.print_name();

}

level: 1This is Chen

class my_emp{public:

my_emp(){name = NULL;age = 0;salary = 0.0;}

my_emp(char *in_name, int in_age, float in_salary):salary(in_salary), age(in_age){name = new char[strlen(in_name)+1];strcpy(name, in_name);}

~my_emp(){ delete [] name; }

void print_name() const{ cout << "This is " << name << endl;}

int get_age () const{ return age; }

float get_salary() const{ return salary; }

private:char *name;int age;float salary;

};

class manager : private my_emp{public:

manager(char *in_name, short in_age, float in_salary, int in_level): my_emp(in_name, in_age, in_salary) { level=in_level; }

void print_level(){ cout << "level: " << level << endl; }

private:int level;

};

void main(){

manager m ("Chen", 0, 0, 1);m.print_level();m.print_name();

}

How about now?

class my_emp{public:

my_emp(){name = NULL;age = 0;salary = 0.0;}

my_emp(char *in_name, int in_age, float in_salary):salary(in_salary), age(in_age){name = new char[strlen(in_name)+1];strcpy(name, in_name);}

~my_emp(){ delete [] name; }

void print_name() const{ cout << "This is " << name << endl;}

int get_age () const{ return age; }

float get_salary() const{ return salary; }

private:char *name;int age;float salary;

};

class manager : private my_emp{public:

manager(char *in_name, short in_age, float in_salary, int in_level): my_emp(in_name, in_age, in_salary) { level=in_level; }

void print_level(){ cout << "level: " << level << endl; }

private:int level;

};

void main(){

manager m ("Chen", 0, 0, 1);m.print_level();m.print_name(); //ILLEGAL!

}

How about now?

class my_emp{public:

my_emp(){name = NULL;age = 0;salary = 0.0;}

my_emp(char *in_name, int in_age, float in_salary):salary(in_salary), age(in_age){name = new char[strlen(in_name)+1];strcpy(name, in_name);}

~my_emp(){ delete [] name; }

void print_name() const{ cout << "This is " << name << endl;}

int get_age () const{ return age; }

float get_salary() const{ return salary; }

private:char *name;int age;float salary;

};

class manager : private my_emp{public:

manager(char *in_name, short in_age, float in_salary, int in_level): my_emp(in_name, in_age, in_salary) { level=in_level; }

void print_level(){ cout << "level: " << level << endl; pirnt_name(); // Call it here!}

private:int level;

};

void main(){

manager m ("Chen", 0, 0, 1);m.print_level();

}

To get the same output

The protected: Qualifier

• New classification of class members

• Allows access "by name" in derived class– But nowhere else– Still no access "by name" in other classes

• In class it’s defined acts like private

• Considered "protected" in derived class– To allow future derivations

class my_emp{public:

my_emp(){name = NULL;age = 0;salary = 0.0;}

my_emp(char *in_name, int in_age, float in_salary):salary(in_salary), age(in_age){name = new char[strlen(in_name)+1];strcpy(name, in_name);}

~my_emp(){ delete [] name; }

void print_name() const{ cout << "This is " << name << endl;}

int get_age () const{ return age; }

float get_salary() const{ return salary; }

protected:char *name;int age;float salary;

};

class manager : public my_emp{public:

manager(char *in_name, short in_age, float in_salary, int in_level): my_emp(in_name, in_age, in_salary) { level=in_level; }

void print_level(){ cout << "level: " << level << endl; cout << “age: " << age << endl; //OK}

private:int level;

};

void main(){

manager m ("Chen", 0, 0, 1);m.age = 25; //ILLEGAL!m.salary = 1000000; //ILLEGAL!m.print_level();m.print_name();

}

Only member functions of

the same class

privateprotected

Member functions of the same class and the derived class

public

Every one !

Constructor of Derived Class

class DerivedClass : public BaseClass{…public:

DerivedClass (argument list) : BaseClass (argument list){}

…};

class DerivedClass : public BaseClass{…public:

DerivedClass (argument list){}

…};

Or

In the initialization section

class my_emp{public:

my_emp(){name = NULL;age = 0;salary = 0.0;}

my_emp(char *in_name, int in_age, float in_salary):salary(in_salary), age(in_age){name = new char[strlen(in_name)+1];strcpy(name, in_name);}

~my_emp(){ delete [] name; }

void print_name() const{ cout << "This is " << name << endl;}

int get_age () const{ return age; }

float get_salary() const{ return salary; }

protected:char *name;int age;float salary;

};

class manager : public my_emp{public:

manager(char *in_name, short in_age, float in_salary, int in_level): my_emp(in_name, in_age, in_salary) { level=in_level; }

void print_level(){ cout << "level: " << level << endl; cout << “age: " << age << endl; //OK}

private:int level;

};

void main(){

manager m ("Chen", 0, 0, 1);m.print_level();m.print_name();

}

The order of constructor callingIn general,

First, the constructor of the base class

Second, the constructor of the object type of member variables

Finally, the constructor of the derived class

class DerivedClass : public BaseClass{…public:

DerivedClass (argument list) : BaseClass (argument list), A (initial value)

{}

…private: A obj; //A is a pre-defined class!};

class my_emp{public:

my_emp(){name = NULL;age = 0;salary = 0.0;}

my_emp(char *in_name, int in_age, float in_salary):salary(in_salary), age(in_age){name = new char[strlen(in_name)+1];strcpy(name, in_name);}

~my_emp(){ delete [] name; }

void print_name() const{ cout << "This is " << name << endl;}

int get_age () const{ return age; }

float get_salary() const{ return salary; }

protected:char *name;int age;float salary;

};

class manager : public my_emp{public:

manager(char *in_name, short in_age, float in_salary, int in_level): my_emp(in_name, in_age, in_salary), level(in_level) { }

void print_level(){ cout << "level: " << level << endl; cout << “age: " << age << endl; //OK}

private:int level;

};

void main(){

manager m ("Chen", 0, 0, 1);m.print_level();m.print_name();

}

The order of the constructor calling and destructor calling for a derived object

B

C

A class A{};

class B : public A{};

class C : public B{};

C obj_c;

when create an object of C

it will call the constructors in the order of A→B →C

when the program (or function) is exiting

it will call the destructors in the order of C→B →A

Redefinition

class BaseClass{public:…

void method1(argument list){//implementation in base class}};

class DerivedClass : public BaseClass{…public:

void method1(same argument list!){//new implementation}void method1(different argument list){//new implementation}

…};

RedefinitionHow to call the member functions of the base class that are re-defined.

class BaseClass{public:…

void method1(argument list){//implementation in base class}};

class DerivedClass : public BaseClass{…public:

void method1(same argument list!){//new implementation}

…};

DerivedClass obj1;obj1.BaseClass::method1(input parameters);

Multiple Inheritance

• The new derived class can inherit from more than one base class

class A{……};

class B{……};

class C : public A, public B{……};

Like single inheritance, the derived class will automatically inherit all the public members from all its parent classes.

Multiple Inheritance

• The new derived class can inherit from more than one base classclass A{……};

class B{……};

class C : public A, public B{……public: C (argument list) : A(arguments), B(arguments) {…}};

Like single inheritance, the derived class will automatically inherit all the public members from all its parent classes.

Be careful about the constructor

Multiple Inheritance

• Problem of multiple inheritanceclass A{……public: int val;};

class C : public A, public B{……};

C c_obj;c_obj.val=0; // which “val”, from A or from B?

Ambiguity of inherited members!

class B{……public: int val;};

Multiple Inheritance

• Problem of multiple inheritanceclass A{……public: int val;};

class C : public A, public B{……};

C c_obj;c_obj.A::val=0; // legalc_obj.B::val=2; // legal as well

Solution – use scope resolution “::”

class B{……public: int val;};

Overall, try to avoid multiple inheritance, as you may not know the ancestors of those base classes.

The configuration to the right could happen!

A

B C

D

A

class A { public:

void vf() {cout<<"I come from class A"<<endl; }

};

class B: public A{};class C: public A{};

class D: public B, public C{};

void main(){

D d;d.vf (); // error, multiple copies of

vf()}

There is indeed one possible solution to address thati.e. using virtual inheritance A

B C

D

A

class A { public:

void vf() {cout<<"I come from class A"<<endl; }

};

class B: virtual public A{};class C: virtual public A{};

class D: public B, public C{};

void main(){

D d;d.vf (); // this OK now

}

Another more complicated example

class B1:virtual public B ,virtual public A{public: B1(int i){ cout<<"Constructing B1"<<endl; }};

class B2: public A, virtual public B {public: B2(int j){ cout<<"Constructing B2"<<endl; }};

class D: public B1, public B2 {public: D(int m,int n): B1(m),B2(n){ cout<<"Constructing D"<<endl;

} A a;}; void main(){ D d(1,2);}