53
1 Chapter 8 Composition

1 Chapter 8 Composition. 2 Outlines 8.1 Composition versus Inheritance 8.2 Using Composition 8.3 Constructing and Destroying Composed Classes 8.4 Combining

Embed Size (px)

Citation preview

1

Chapter 8

Composition

2

Outlines

• 8.1 Composition versus Inheritance

• 8.2 Using Composition

• 8.3 Constructing and Destroying Composed Classes

• 8.4 Combining Composition and Inheritance

• 8.5 Case Study: Computer System Configurator

3

8.1 Composition vs. Inheritance

• Code reusability can be promoted in C++ by establishing relationships between classes.

• A new kind of relationship between classes called composition.

• An AND gate is a type of gate, and a gate is a type of logic circuit. Therefore, an AND gate class is derived from a gate class and the gate class is derived from a logic circuit class.

• Sometimes an object is not a type or subgroup of another object.

4

AND->GATE->LC

5

Composition

• C++ provides another relationship between classes called composition, which is also known as a has‑a relationship.

• For example, a CPU is not a type of PC, howev er, a PC has a CPU as well as many other components. Therefore, a PC class could be composed of a CPU class and some other classes.

• A new class can be composed from as many existing classes and built‑in types as an application requires.

6

Has-a relationship

7

Class declaration• To compose a class from existing classes, an object of each

class should be declared as a member of the new class.

• Storage class, which is composed of the three classes Hard_Disk, RAM, and Floppy

Class Storage //composed class

{

float capacity;

Hard Disk hdst; //an object of the Hard Disk class

RAM ramst; //an object of the RAM class

Floppy fpst; //an object of the Floppy class

Public:

Storage();

~Storage();

};

8

Composition• Classes that are used to build a composed cl

ass can be referred to as subclasses and their objects as embedded objects, or subobjects.

• In addition to the three embedded objects, hdst, ramst, and fpst, the Storage class contains a built‑in type as well.

• The embedded objects are private members of the Storage class. They can be designed as public members as well.

9

8.2 Using Composition

1. //PROG8_1: Program demonstrates a composed class2. // with embedded objects as public members.3. #include <iostream>4. using namespace std;5. class Pixel{ //subclass6. int x, y; //x and y coordinates of a pixel7. public:8. Pixel(){ x=0; y=0; }9. void set(int a, int b) { x=a; y=b; }10. int getx() const { return x; }11. int gety() const { return y; }12. };13. class Rectangle{ //composed class as ** public member14. int perimeter;15. public:16. Pixel p1, p2; //embedded objects as public members17. Rectangle(){ perimeter=0; }18. void getperim();19. };

10

1. void Rectangle::getperim()2. {3. int x1 = p1.getx(); //Calls the embedded objects'4. int x2 = p2.getx(); //member functions5. int y1 = p1.gety();6. int y2 = p2.gety();7. cout<<"Top-left corner coordinates : [";8. cout<<x1<<','<<y1<<']'<<endl;9. cout<<"Bottom-right corner coordinates : [";10. cout<<x2<<','<<y2<<']'<<endl;11. cout<<"Perimeter = "<<(2*(x2-x1)+2*(y2-y1));12. }13. int main()14. {15. Rectangle r; //composed object16. r.p1.set(5,5); //Using composed object to access17. r.p2.set(10,10); //members of the embedded objects.18. r.getperim();19. return 0;20. }

11

Note

• The security of the subclass's private members is maintained, even if the embedded objects have public access in the composed class.

• Each level of objects or sub-objects is separated by a dot (.) separator.composed_object.subobject.subobject_member

• The composed class can only access public members of its subclass.

• Access to the private members of the subclass is limited to the members of that subclass.

• The public members of the subclass, in this case, cannot be accessed outside of the composed class through the objects of the composed class.

12

13

1. //PROG8_2: Program demonstrates composition with embedded 2. // objects as private members.3. #include <iostream>4. using namespace std;

5. class Pixel {6. int x, y;7. public:8. Pixel(){ x = 0; y = 0; }9. void set(int a, int b) { x = a; y = b; }10. int getx() const { return x; }11. int gety() const { return y; }12. };13. class Rectangle {14. int perimeter;15. Pixel p1, p2; //embedded objects as private members16. public:17. Rectangle(){ perimeter = 0; }18. void getperim();19. };

14

1. void Rectangle::getperim()2. {3. p1.set(5,5); //Calls the embedded objects'4. p2.set(10,10); //interface functions5. int x1 = p1.getx();6. int x2 = p2.getx();7. int y1 = p1.gety();8. int y2 = p2.gety();9. cout<<"Top-left corner coordinates : [";10. cout<<x1<<','<<y1<<']'<<endl;11. cout<<"Bottom-right corner coordinates : [";12. cout<<x2<<','<<y2<<']'<<endl;13. cout<<"Perimeter = "<<(2*(x2-x1)+2*(y2-y1));14. }15. int main()16. {17. Rectangle r; //Instantiates a composed object and18. r.getperim(); //calls its interface function19. return 0;20. }

15

16

Note• If the embedded objects are created as private

members of the composed class, their public interface functions are used to communicate with the composed class.

• The interface between the classes and the rest of the program in this case is established through the public interface functions of the composed class. It makes the program more maintainable, simplifies its logic, and provides a safer interface.

• Safer, in this context, means that the embedded objects are hidden from the rest of the program and can only be accessed by the members of its composed class.

17

8.3 CONSTRUCTING AND DESTROYING COMPOSED CLASSES

• If a class is composed from one or more subclasses, the subclass embedded objects will be constructed first when the composed class is instantiated.

• Use default constructor functions. If a subclass uses a non‑default constructor function with arguments, the programmer must define a composed class constructor with a constructor initialization list.

• If this is not done, the compiler may not be able to construct composed objects.

18

• A constructor initialization list should be designed if the subclasses have constructors with arguments.

• The names of the embedded objects are used instead of the names of the base classes to pass values between constructors.

19

• Format:composed‑constructor( parameter list ):subobj1(values1),subobj2(values2),...,subobjn(valuesn){//body of the composed constructor}• A parameter list of the composed class constructor shoul

d include all of the arguments needed to initialize the data members of the composed class, as well as the arguments needed to initialize its embedded objects.

20

Example

Rectangle(int a, int b, int c, int d) : p1(a, b), p2(c, d){perimeter = 0;}• The constructors needed to initialize the p1 and p2 sub-o

bjects are called before the execution of the body of the composed class constructor.

• It is logical because it is necessary to build the sub-objects first in order to build an overall composed class object.

• The order of the constructor calls is not affected by the order of the embedded objects in the constructor initialization list.

21

• The same syntax can also be used if a class is composed of built‑in types,

class Circuit {char * type; int quantity; float price;public: Circuit(char *t, int q, float p): type(t), quantity(q), price(p) };

22

1. //PROG8_3: Program demonstrates the order of the constructor and

2. // destructor calls when constructing and destroying a

3. // composed object.

4. #include <iostream>

5. using namespace std;

6. class Speaker { //subclass #1

7. float impedance;

8. public:

9. Speaker(float imp) //constructor #1

10. {

11. impedance = imp;

12. cout<<"Constructing speaker."<<endl;

13. }

14. float getimp()const { return impedance; }

15. ~Speaker() //destructor #1

16. {

17. cout<<"Destroying speaker."<<endl;

18. }

19. };

23

1. class Amplifier { //subclass #22. float impedance;3. public:4. Amplifier(float imp ) //constructor #25. {6. impedance = imp;7. cout<<"Constructing amplifier."<<endl;8. }9. float getimp()const { return impedance;}10. ~Amplifier() //destructor #211. {12. cout<<"Destroying amplifier."<<endl;13. }14. };

24

1. class Stereo { //composed class2. Speaker sp; //embedded object #13. Amplifier amp; //embedded object #24. public:5. Stereo(float x, float y):sp(x), amp(y)6. { //composed constructor7. cout<<"Constructing stereo."<<endl;8. }9. void matching() //Compares the impedances10. {11. if(sp.getimp()==amp.getimp())12. cout<<"Impedances are matched."<<endl;13. else14. cout<<"Impedances are not matcheed."<<endl;15. }16. ~Stereo() //composed destructor17. {18. cout<<"Destroying stereo."<<endl;19. }20. };

25

1. main()

2. {

3. Stereo st(8, 8); //composed object

4. st.matching();

5. return 0;

6. }

26

• Please note that the output of PROG8 3 will not change even if the order of the embedded objects is changed in the constructor initialization list as follows:;

Stereo(float x, float y): amp(x), sp(y)

{

cout«"Constructing stereo."« endl;

}

27

• A constructor of a composed class can also take objects as arguments.

• Doing this results in a much shorter list of arguments, whereas the constructors of the embedded objects require many individual arguments.

• A composed class can contain pointers to sub-objects as members.

• This class may use its constructor function to dynamically allocate sub-objects and store their addresses in the member pointers. Note that the composed class destructor must free memory dynamically allocated by the constructor.

28

1. //PROG8_4: Program demonstrates a composed class containing

2. // pointers to subobjects as members.

3. #include <iostream>

4. using namespace std;

5. class Speaker { //subclass #1

6. float impedance;

7. public:

8. Speaker() //constructor #1

9. {

10. cout<<"Constructing speaker."<<endl;

11. }

12. void setimp(float imp) { impedance=imp; }

13. float getimp()const { return impedance; }

14. ~Speaker() //destructor #1

15. {

16. cout<<"Destroying speaker."<<endl;

17. }

18. };

29

1. class Amplifier { //subclass #22. float impedance;3. public:4. Amplifier() //constructor #25. {6. cout<<"Constructing amplifier."<<endl;7. }8. void setimp(float imp) { impedance=imp; }9. float getimp()const { return impedance; }10. ~Amplifier() //destructor #211. {12. cout<<"Destroying amplifier."<<endl;13. }14. };

30

1. class Stereo { //composed class2. Speaker *ptsp; //pointer to subobject #13. Amplifier *ptamp; //pointer to subobject #24. public:5. Stereo(Speaker &s, Amplifier &a)6. { //composed constructor7. ptsp=new Speaker;8. ptamp=new Amplifier;9. *ptsp=s;10. *ptamp=a;11. cout<<"\tConstructing stereo."<<endl;12. }13. void matching() //Compares the impedances14. {15. if(ptsp->getimp()==ptamp->getimp())16. cout<<"Impedances are matched."<<endl;17. else18. cout<<"Impedances are not matcheed."<<endl;19. }

31

1. ~Stereo() //composed destructor2. {3. delete ptsp;4. delete ptamp;5. cout<<"\tDestroying stereo."<<endl;6. }7. };8. int main()9. {10. Amplifier a;11. a.setimp(8);12. Speaker s;13. s.setimp(8);14. Stereo st(s, a); //composed object15. st.matching();16. return 0;17. }

32

Output

33

8.4 COMBINING INHERITANCE AND COMPOSITION

34

1. //PROG8_5: Program demonstrates a use of inheritance //and

2. //composition combined together to build a complex class.

3. #include <iostream>4. using namespace std;5. class Pixel { //subclass6. int x, y;7. public:8. Pixel(int a, int b) //constructor9. {10. x = a;11. y = b;12. cout<<"SubConstructor"<<" x="<<x<<" y="<<y<<endl;13. }14. ~Pixel(){cout<<"SubDestructor"<<endl;} //destructor15. };

35

1. class RecShape { //base class protected:

2. int lg, wd;

3. public:

4. RecShape(int l, int w) //constructor

5. {

6. lg = l;

7. wd = w;

8. cout<<"BaseConstructor"<<" lg="<<lg<<" wd="<<wd<<endl;

9. }

10. ~RecShape(){cout<<"BaseDestructor"<<endl;} //destructor

11. };

36

1. class Rectangle:public RecShape { //derived & composed class

2. int perimeter;3. Pixel p1, p2; //subobjects4. public:5. Rectangle(int x1,int y1,int x2,int y2): //constructor6. RecShape(x2-x1,y2-y1),p1(x1,y1),p2(x2,y2)7. {8. perimeter = 0;9. cout<<"CombConstructor"<<" x1="<<x1<<" y1="<<y1;10. cout<<" x2="<<x2<<" y2="<<y2<<endl;11. }12. void getperim()13. {14. cout<<"Perimeter = "<<(2*lg + 2*wd)<<endl;15. }16. ~Rectangle(){cout<<"CombDestructor"<<endl;} //destr

uctor17. };

37

• int main()• {• Rectangle r(5,5,10,10);• r.getperim();• return 0;• }

38

39

40

41

42

CASE STUDY: COMPUTER SYSTEM CONFIGURATOR

• INPUT/OUTPUT • Input:

– A selection of parts from the menu options offered

– A choice of one of the following options:• Display the current system configuration• Change a part• Exit the program

• output: – A list of all the parts selected, including the cost

of each part and the total cost.

43

• //CASEST8: The program demonstrates a practical use of • // composition to configure a computer system.• #include<iostream>• #include<iomanip>• using namespace std;

• class CPU //Subclass CPU• {• char *bcpu[3]; //CPU options• float pcpu[3]; //costs of CPU options• public:• CPU() { //CPU constructor• bcpu[0]="Pentium III 866 MHz ";• bcpu[1]="Pentium IV 1.4 GHz ";• bcpu[2]="Pentium IV 2.0 GHz ";• pcpu[0]=150; pcpu[1]=270; pcpu[2]=450;• }• friend ostream &operator <<(ostream &out, CPU &a);• void select_cpu(char *temp[1], float &cost);• };

44

• ostream &operator <<(ostream &out, CPU &a)• {• out<<"\n\n CPU \t\t\t\tCOST\n" ;• out<<"-------------------------------------\t\t\t------- \n";• for(int i = 0; i<3; i++)• {• out<<setiosflags(ios::left | ios::fixed)<<setprecision(2);• out<<(i+1)<<") "<<setw(64)<<a.bcpu[i];• out<<"\t$"<<a.pcpu[i]<<endl;• out<<'\n';• }• return out;• }• void CPU::select_cpu(char *temp[1], float &cost) //Selects a CPU• {• int select;• cout<<'\n';• do{• cout<<"Select a CPU from the above ==> ";• cin>>select;• }while((select<1) ||( select>3));• temp[0] = bcpu[select-1];• cost = pcpu[select-1];• }

45

• class RAM { //Subclass RAM• char *bmem[3]; //RAM options• float pmem[3]; //costs of RAM options• public:• RAM() { //RAM constructor• bmem[0]="64 MB ";• bmem[1]="128 MB ";• bmem[2]="256 MB ";• pmem[0]=39.99; pmem[1]=68; pmem[2]=152;• }• friend ostream &operator <<(ostream &out, RAM b);• void select_ram(char *temp[1], float &cost);• };• ostream &operator <<(ostream &out, RAM b) //Displays RAM options• {• out<<"\n\n RAM \t\t\t\tCOST\n" ;• out<<"------------------------------------\t\t\t------- \n";• for(int i=0; i<3; i++)• {• out<<setiosflags(ios::left | ios::fixed)<<setprecision(2);• out<<(i+1)<<")"<<setw(62)<<b.bmem[i];• out<<"\t$"<<b.pmem[i]<<endl;• out<<'\n';• }• return out;• }

46

• void RAM::select_ram(char *temp[1], float &cost) //Selects a RAM• {• int select;• cout<<"\n";• do{• cout<<"Select RAM from the above ==> ";• cin>>select;• }while((select<1)||( select>3));• temp[0] = bmem[select-1];• cost = pmem[select-1];• }

• class Hard_Drive { //Subclass Hard_Drive• char *bhd[4];• float phd[4];• public:• Hard_Drive() {• bhd[0]="10.0 GB ";• bhd[1]="15.3 GB";• bhd[2]="30.0 GB";• bhd[3]="40.0 GB";• phd[0]=110; phd[1]=124; phd[2]=174; phd[3]=220;• }• friend ostream &operator <<(ostream &out, Hard_Drive &c);• void select_hdrive(char *temp[1], float &cost);• };

47

• ostream &operator <<(ostream &out, Hard_Drive &c)• {• out<<"\n\n HARD DRIVE \t\t\t\tCOST\n" ;• out<<"-------------------------------------\t\t\t------- \n";• for(int i=0; i<4; i++)• {• out<<setiosflags(ios::left | ios::fixed)<<setprecision(2);• out<<(i+1)<<") "<<setw(62)<<c.bhd[i];• out<<"\t$"<<c.phd[i]<<endl;• out<<'\n';• }• return out;• }• void Hard_Drive::select_hdrive(char *temp[1], float &cost)• {• int select;• cout<<'\n';• do{• cout<<"Select a hard drive from the above ==> ";• cin>>select;• }while((select<1)||( select>4));• temp[0] = bhd[select-1];• cost = phd[select-1];• }

48

• class PC { //Composed class• char *parts[3]; //parts selected by the user• float pcost[3]; //costs of the selected parts• float total; //total cost of the system• CPU cpu; //subobjects• RAM ram;• Hard_Drive hd;• public:• PC();• void display_cpu()• {• cout<<cpu;• cpu.select_cpu(& parts[0], pcost[0]);• }• void display_mem()• {• cout<<ram;• ram.select_ram(& parts[1], pcost[1]);• }• void display_hdrive()• {• cout<<hd;• hd.select_hdrive(& parts[2], pcost[2]);• }• friend ostream &operator <<(ostream &out, PC a);• };

49

• PC::PC() //PC constructor• {• total=0;• for(int i=0;i<3;i++)• {• parts[i] = "Not Selected";• pcost[i] = 0;• }• }• ostream &operator <<(ostream &out, PC a)• { //Displays the system chosen and total cost• for(int i=0;i<3;i++)• a.total = a.total + a.pcost[i];• out<<setiosflags(ios::left | ios::fixed)<<setprecision(2);• out<<" PART TYPE \t\t\tCOST\n ";• out<<"-----------------------------------\t\t\t------"<<endl;• out<<'\n'<<setw(15)<<"CPU:"<<setw(54)<<a.parts[0];• out<<"\t$"<<a.pcost[0]<<endl;• out<<'\n'<<setw(15)<< "RAM:"<<setw(54)<<a.parts[1];• out<<"\t$"<<a.pcost[1]<<endl;• out<<'\n'<<setw(15)<<"Hard drive:"<<setw(54)<<a.parts[2];• out<<"\t$"<<a.pcost[2]<<endl;• out<<"\n\n\t\t\t\t\tThe total cost of the system===>$";• out<<a.total<<endl;• return out ;• }

50

• char menu()• {• char selection;• cout<<"\n\n\n \t\t\t Computer System "<<endl;• cout<<"\t\t-------------------------------------------\n\n";• cout<<"\t\ta) Select CPU \n";• cout<<"\t\tb) Select RAM \n";• cout<<"\t\tc) Select Hard Drive\n";• cout<<"\t\td) Display the system and total cost\n";• cout<<"\t\tx) Exit\n\n";• cout<<"\t\t Enter your choice here ===> ";• cin>> selection;• return selection;• }

51

• int main()• {• char selection;• PC computer; //Composed object• do {• selection=menu();• switch(selection)• {• case 'a': computer.display_cpu(); break;• case 'b': computer.display_mem(); break;• case 'c': computer.display_hdrive(); break;• case 'd':• cout<<" The system chosen : \n\n";• cout<<computer;• break;• case 'x': cout<<"\n\n\n\t\tEnd of program."; break;• default: cout<<"\n\t Incorrect input! ";• }• } while(selection!='x');• return 0;• }

52

53