190 likes | 218 Views
Class Relationships in C++. CS 123/CS 231. Class Diagram Notation Revisited. Multiplicity Association and Navigability Composition Aggregation. Mandatory Relationships. class Car { private: Engine engine; … }. Car. Engine. 1. engine. class Department { private:
E N D
Class Relationships in C++ CS 123/CS 231
Class Diagram NotationRevisited • Multiplicity • Association and Navigability • Composition • Aggregation
Mandatory Relationships class Car { private: Engine engine; … } Car Engine 1 engine class Department { private: Employee *head; … Department(); // no default public: Department(Employee& h) { head = &h; } … } Department Employee 1 head
Optional Relationship class Book { private: Patron *borrower; … public: Book() { borrower = NULL; } void setBorrower(Patron& p) { borrower = &p; } void returnBook() { borrower = NULL; … } … } Patron Book 0..1 borrower
Multiple and Mandatory Car Wheel 4 wheels // Option 1: Array of objects // use for composition relationships class Car { private: Wheel wheels[4]; ... } // Option 2: Array of pointers class Car { private: Wheel *wheels[4]; // wheel objects may be created externally ... Car(); public: Car(Wheel& w1, Wheel& w2, … ) … // wheel references required in constructor }
Multiple but not Mandatory class Patron { private: Book *borrowed[3]; int numBooks; // counter … public: Patron() { numBooks = 0; } void borrow(Book& b) { if (numBooks < 3) borrowed[numBooks++] = &b; … } … } Patron Book 0..3 borrowed
“One-to-Many” Relationships • Collection required but size is not fixed • Dynamically allocate the array • Use a pointer and new to create the array • Use pointer-to-pointers-to-objects if you want to handle references; use pointer-to-objects if you want to create the objects within the class (composition). • Or, use a container data structure • e.g., a list structure that maintains a collection of pointers (or objects)
“One-to-Many” Examples Invoice Order Line * lines Teacher Student 1 * advisor advisees Catalog Book * booklist
Using a Variable-sized Array class Catalog { private: Book *booklist; int capacity; int listSize; … public: Catalog(int startSize) { booklist = new Book[startSize]; // note that Book objects are created here capacity = startSize; listSize = 0; } void addBook(Book& b) { if (listSize < capacity) booklist[listSize++] = b; // question: what happens here? else // resize array and then add } … } Catalog addBook() Book * booklist
Implementing Collections through a Data Structure Note: Although it is not incorrect to include the LinkedList class (and other details) in a class diagram, it is often not necessary because it is more of an implementation detail on how a collection is carried out. class Catalog { private: LinkedList booklist; … public: void addBook(Book& b) { booklist.insert(b); } … } Catalog Book * versus Catalog 1 Linked List Node 1 Book next
Associations and Navigability • Given two classes A and B that are associated • Navigability is the ability to access associated objects from a given class • Can provide a way to get to the B objects from A, to the A objects from B, or both • Note: we provided both directions for the Patron-Book example, but we did not have to • Decision often depends on the use cases • Or, create an association class
Common Implementations for Associations • For 1-to-1 relationships, one-way navigability is often sufficient • Example: Employee and Spouse • For 1-to-Many relationships, allow navigation at least from the “Many” participant • Example: Faculty-Advisor and Student • For Many-to-Many relationships, provide two directions, or create an association class *Note that there are efficiency and update considerations/tradeoffs
Association Class Student Course * * Enrollment grade Student Enrollment grade Course * * What about navigability?
Navigability and Association Classes class Student { private: … Enrollment **list; // collection of enrollment records // for the student (may be useful) … } class Course { private: … Enrollment **list; // collection of enrollment records // for the course (useful?) … } class Enrollment { private: Student *student; Course *course; double grade; … }
Navigability and Associative Maps • In collection classes for associations, it is also helpful to use maps (key-object pairs) instead of a list of just the objects • To facilitate searching for objects in an association based on a search key • For example, in the collection class for Enrollment, we could have maps that use student-id and/or course-number as keys • Carrying out the operation “list all courses and grades that student 222-11-0000 has taken” is now possible without the list member in student
Composition • Recall that the essence of composition is that the part is created and destroyed with the whole • It is thus “natural” (at least in C++) to use object members to implement a composition relationship • But it is possible to use pointers to part-objects and let the constructors and the destructor take care of the consistent creation and destruction of the parts
Aggregation • Standing recommendation: use pointers to represent the parts and ensure that object construction of the whole requires the user to specify the parts • Hide the default constructor and have a user-defined constructor whose parameters are references to the parts • Parts can be created externally, but need to be included when creating the whole • Problem: It is still possible (within the class) to have a whole without the part
Problem with Mandatory Parts and Object Pointers class Car { private: Engine *engine; Car(); public: Car(Engine& e); … } Car Engine 1 engine Problem: Programmer can still perform engine = NULL; within this class
Option: Using Members as &-References (aliases) class Car { private: Engine &engine; Car(); public: Car(Engine& e): engine(e) { … } … // can update engine here // but it can’t be “nullified” } Solution: By using a &-reference as a member, enginehas to refer to an actual Engine object