Aggregation

 < Day Day Up > 



Class behavior can be implemented by building upon the behavior provided by other classes. In other words, a class type object can contain other class type objects. A class built upon the functionality of other classes is referred to as an aggregate class type or aggregation. There are two types of aggregation: simple and composite. Aggregation is also referred to as a “has-a” or a “uses-a” relationship between the whole class and its part classes where the whole or aggregate class uses the services of its part classes.

Simple vs. Composite Aggregation

The aggregation relationship is expressed in terms of the whole class and the part class. Given two classes, class B and class A, if class B contains class A then class B is referred to as the whole class and class A is referred to as the part class.

The Relationship Between Aggregation and Object Lifetime

The primary difference between simple and composite aggregation lies in who controls the lifetime of the object.

Simple Aggregation

A simple aggregate object does not control the lifetimes of its part objects. Part objects involved in simple aggregations can be associated with more than one aggregation.

Composite Aggregation

A composite aggregate object controls the lifetimes of its part objects. Part objects involved in composite aggregations cannot be associated with more than one aggregation.

Aggregation Example Code

Example 12.1 gives the class declarations for class A that will be used to demonstrate the two different types of aggregation. Class A will be the contained or part class. Example 12.2 shows the implementation code for class A which would be in a file named a.cpp

Listing 12.1: a.h

start example
1  #ifndef _CLASS_A_H 2  #define _CLASS_A_H 3 4  class A { 5    public: 6    A(); 7    ~A(); 8  }; 9  #endif
end example

Listing 12.2: a.cpp

start example
 1  #include <iostream>  2  using namespace std;  3  #include "a.h"  4  5  A::A(){  6     cout<<"An object of type A created!"<<endl;  7  }  8  9  A::~A(){ 10      cout<<"An object of type A destroyed!"<<endl; 11  }
end example

Class A is relatively simple. All it does is print a message to the screen when a class A object is created and destroyed. These messages will come in handy for learning about the behavior of aggregate objects.

Composite Aggregation Example

Example 12.3 gives the class declaration for class B containing a class A object. Class B is the aggregate or whole class and class A is the part class.

Example 12.3: b.h

start example

click to expand

end example

Said another way, an object of type B has an object of type A.

Example 12.4 shows the implementation code for class B. It looks exactly like the implementation code for class A except the name of the class has changed.

Listing 12.4: b.cpp

start example
 1  #include <iostream>  2  using namespace std;  3  #include "b.h"  4  5  B::B(){  6     cout<<"An object of type B created!"<<endl;  7  }  8  9  B::~B(){ 10     cout<<"An object of type B destroyed!"<<endl; 11  }
end example

Example 12.5 gives the code for a main() function that creates a class B object and figure 12-1 shows the results obtained from running the program.

Listing 12.5: main.cpp

start example
1  #include <iostream> 2  using namespace std; 3  #include "b.h" 4 5  int main(){ 6     B b1; 7     return 0; 8  }
end example

click to expand
Figure 12-1: Results of Running Example 12.5

Let us pause here for a moment of discussion. Study figure 12-1. Notice how the A object’s constructor was called prior to the B object’s constructor. From this experiment we can deduce that ordinary part objects will be created before the whole aggregate object is created. An important distinction to make here is that object creation is not complete until the constructor has finished executing. The important thing to take away from this example is that the life of a part object is controlled by the composite aggregate whole object. Notice in example 12.5 that only a B object is created. This causes the creation of B’s part object. For an object with part members to be fully created, all of its part members must first be created.

Another Composite Aggregation Example

The B class will be slightly modified to show another why to create aggregate objects using pointers. Example 12.6 gives the code for the modified class B declaration.

Listing 12.6: b.h

start example
 1  #ifndef _CLASS_B_H  2  #define _CLASS_B_H  3  #include "a.h"  4  5  class B{  6   public:  7     B();  8     ~B();  9   private: 10     A *its_a_ptr; 11  }; 12  #endif
end example

The only change made to the B class declaration appears on line 10. B’s private data member was changed from an A object to a pointer to an A object. The name of the identifier was also changed to reflect its new role as a pointer.

Example 12.7 shows the modified class B implementation code.

Listing 12.7: b.cpp

start example
 1  #include <iostream>  2  using namespace std;  3  #include "b.h"  4  5  B::B(){  6     its_a_ptr = new A();  7     cout<<"An object of type B created!"<<endl;  8  }  9 10  B::~B(){ 11      delete its_a_ptr; 12      cout<<"An object of type B destroyed!"<<endl; 13  }
end example

Several changes were made to this file. First, the code on line 6 was added to the constructor to explicitly create the A object and assign its address to its_a_ptr. The second change is to the destructor. The code on line 11 was added to explicitly call the A object’s destructor by deleting the pointer.

The result of running example 12.5 again is shown in figure 12-2.

click to expand
Figure 12-2: Results of Running Example 12.5 Again

The order of the messages in figure 12-2 is of minor importance. Special code is added to the constructor and destructor to create and destroy B’s A object. Let us now take a look at a simple composite class.

Simple Aggregation Example

To demonstrate simple aggregation the A and B class files will be once again modified. Example 12.8 gives the source code for the revised a.h file.

Listing 12.8: a.h

start example
 1  #ifndef _CLASS_A_H  2  #define _CLASS_A_H  3  4  class A {  5    public:  6           A();  7          ~A();  8    void sayHi();  9  }; 10  #endif
end example

The only change to the A class declaration is the addition of another public function on line 8 named sayHi(). The modified a.cpp file is shown in example 12.9.

Listing 12.9: a.cpp

start example
 1  #include <iostream>  2  using namespace std;  3  #include "a.h"  4  5  A::A(){  6     cout<<"An object of type A created!"<<endl;  7  }  8  9  A::~A(){ 10     cout<<"An object of type A destroyed!"<<endl; 11  } 12 13  void A::sayHi(){ 14     cout<<"Hi!"<<endl; 15  }
end example

The sayHi() function definition begins on line 13. All it will do is print a simple message to the screen. Now, in addition to the constructor and destructor messages, any object that contains an A object will be able to call the A object’s sayHi() function. The modified B class declaration is given in example 12.10.

Listing 12.10: b.h

start example
 1  #ifndef _CLASS_B_H  2  #define _CLASS_B_H  3  #include "a.h"  4  5  class B{  6   public:  7     B(A *a_ptr);  8     ~B();  9     void makeContainedObjectSayHi(); 10   private: 11     A *its_a_ptr; 12  }; 13  #endif
end example

Two modifications were made to the B class declaration. A parameter is added to the B constructor function of type pointer to A. When a B object is created it will expect to be passed the address of an A object. The second modification is the addition of one additional public function named makeContainedObjectSayHi(). This seems to be a little long winded for a function name but it accurately reflects the purpose of the function and hints at its intended behavior when called. The modified b.cpp implementation file is shown in example 12.11.

Listing 12.11: b.cpp

start example
 1  #include <iostream>  2  using namespace std;  3  #include "b.h"  4  #include "a.h"  5  6  B::B(A *a_ptr):its_a_ptr(a_ptr){  7     cout<<"An object of type B created!"<<endl;  8  }  9 10  B::~B(){ 11     cout<<"An object of type B destroyed!"<<endl; 12  } 13 14  void B::makeContainedObjectSayHi(){ 15     if(its_a_ptr != NULL) 16         its_a_ptr->sayHi(); 17  }
end example

Several modifications were made to b.cpp. First, the code to create and destroy the A object from the constructor and destructor was removed. Since a pointer to an A object will be passed to a B object when one is created that code was no longer required. This emphasizes that the lifetimes of A objects are clearly not at the mercy of B objects. Next, the constructor was modified to add an initializer list to initialize the A class pointer data member named its_a_ptr. Lastly, the makeContainedObjectSayHi() function is implemented beginning on line 14. Notice that just a touch of error checking was introduced. If, for some reason, a B object is fed a NULL pointer when it is created, it would be a mistake to try and call any functions using its_a_ptr since it would be initialized to NULL. If its_a_ptr is not a NULL value then the sayHi() function is called on the contained-by-reference object via the shorthand pointer member access operator “->”.

All that is left now is to look an a main() function that uses the new versions of the A and B classes. Example 12.12 gives the code.

Listing 12.12: main.cpp

start example
 1  #include <iostream>  2  using namespace std;  3  #include "b.h"  4  #include "a.h"  5  6  int main(){  7     A a1;  8     a1.sayHi();  9     B b1(&a1); 10     b1.makeContainedObjectSayHi(); 11     return 0; 12  }
end example

Starting on line 7, an A object named a1 is created and on the next line the sayHi() function is called to demonstrate the existence of the A object outside of the B object. Next, a B object is created and its constructor is called with the address of the A object obtained by using the & operator. On line 10 the makeContainedObjectSayHi() function is called on the B object. This in turn calls the sayHi() function by using the pointer to the A object as shown on line 16 of example 12.11 above.



 < Day Day Up > 



C++ for Artists. The Art, Philosophy, and Science of Object-Oriented Programming
C++ For Artists: The Art, Philosophy, And Science Of Object-Oriented Programming
ISBN: 1932504028
EAN: 2147483647
Year: 2003
Pages: 340
Authors: Rick Miller

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net