.. highlight:: c++ :linenothreshold: 5 ************ Object Array ************ Motivation ========== Array of objects is an efficient method to model a list of records. Compared to other alternative methods to model multiple records, object array excels in many aspects. 1D Array of Objects =================== When modeling a list of ``N`` records with ``M`` fields (a ``N * M`` 2D table), there are several data structures to choose from: Multiple arrays --------------- + store data in ``M`` arrays, each with size ``N``, same index means same record E.g. grades[i] and names[i] refers to the grade and name of a same student :: const int NUM_STUDENT = 100; // max number of students double grades[NUM_STUDENT]; string names[NUM_STUDENT]; names[4] = "John Smith"; grades[4] = 100; cout << "The 5th student in the list " << names[4]; cout << " has a grade of " << grades[4]; + old-fashion approach inherited from the C language Array of objects ---------------- + Pre-allocate ``N`` empty objects using the default constructor + Each object has ``M`` fields + Local array example:: // struct example struct StudentStruct { double grade; string name; }; StudentStruct students[NUM_STUDENT]; students[4].name = "John Smith"; students[4].grade = 100; cout << "The 5th student in the list " << students[4].name; cout << " has a grade of " << students[4].grade; // class example class StudentClass { double grade; string name; public: StudentClass(): name(""), grade(0) {} StudentClass(string name, double grade): name(name), grade(grade) {} string getName() { return name;} double getGrade() { return grade;} }; StudentClass students1[NUM_STUDENT]; // default constructor called 100 times students1[4] = StudentClass("John Smith", 100); // disposable object assignment cout << "The 5th student in the list " << students1[4].getName() << " has a grade of " << students1[4].getGrade(); + To update the fields of the object after the initial allocation of empty objects, you can either #. use assignment or setters:: // if name and grade fields are public students[4].name = "John Smith"; students[4].grade = 100; // if name and grade fields are accessible only through setters students1[4].setName("John Smith"); students1[4].setGrade(100); * fast, modifying existing objects * can be very verbose given large number of fields #. assign temporary object:: students1[4] = Student("John Smith", 100); * overhead of creating and duplicating temporary objects * cleaner syntax + Dynamic array example:: StudentClass *students2; students2 = new StudentClass[NUM_STUDENT]; students1[4] = StudentClass("John Smith", 100); // temporary object assignment cout << "The 5th student in the list " << students1[4].getName() << " has a grade of " << students1[4].getGrade(); delete [] students2; Array of pointers to objects ============================ + Pre-allocate ``N`` pointers + Initialized to ``nullptr`` + Use ``new`` operator to allocate objects as needed + Each object has ``M`` fields + Complex syntax than array of objects + Local array example:: StudentClass *students3[NUM_STUDENT]; // 100 pointers for (int i = 0; i < NUM_STUDENT; ++i) students3[i] = nullptr; students3[4] = new StudentClass("John Smith", 100); cout << "The 5th student in the list " << students3[4]->getName(); cout << " has a grade of " << students3[4]->getGrade(); // release memory for (int i = 0; i < NUM_STUDENT; ++i) delete students3[i]; // delete objects, no [] + Dynamic array example:: StudentClass **students4; // do not confuse with 2D array of objects students4 = new StudentClass *[NUM_STUDENT]; // 100 pointers for (int i = 0; i < NUM_STUDENT; ++i) students4[i] = nullptr; students4[4] = new StudentClass("John Smith", 100); cout << "The 5th student in the list " << students4[4]->getName(); cout << " has a grade of " << students4[4]->getGrade(); // release memory for (int i = 0; i < NUM_STUDENT; ++i) delete students4[i]; // delete objects, no [] delete [] students4; .. graphviz:: digraph G { nodesep=.05; rankdir=LR; objArray[shape=box,height=0.1]; node[shape=record,width=0.1,height=0.1]; first[label="<f0>|<f1>|<f2>|<f3>|<f4>|<f5>|<f6>|<f7>|<f8>|<f9>"]; objArray -> first:f0; sec0[label=""]; sec1[label=""]; sec2[label=""]; sec3[label=""]; sec4[label=""]; sec5[label=""]; sec6[label=""]; sec7[label=""]; sec8[label=""]; sec9[label=""]; first:f0 -> sec0:w; first:f1 -> sec1:w; first:f2 -> sec2:w; first:f3 -> sec3:w; first:f4 -> sec4:w; first:f5 -> sec5:w; first:f6 -> sec6:w; first:f7 -> sec7:w; first:f8 -> sec8:w; first:f9 -> sec9:w; } .. obj-array-pitfalls: Pitfalls ======== + mismatched assignment:: StudentClass students[10]; students[4] = new StudentClass("Mary Johnson", 100); // should have no 'new' operator + wrong member accessing operator:: StudentClass *students[10]; students[4] = new StudentClass("Mary Johnson", 100); cout << students[4].getName() << endl; // should use -> given students[4] as a pointer + confused ``delete`` and ``delete []`` + not checking nullptr before delete