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

    1const int NUM_STUDENT = 100; // max number of students
    2double grades[NUM_STUDENT];
    3string names[NUM_STUDENT];
    4
    5names[4] = "John Smith";
    6grades[4] = 100;
    7cout << "The 5th student in the list " << names[4];
    8cout << " 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:

     1// struct example
     2struct StudentStruct {
     3  double grade;
     4  string name;
     5};
     6
     7StudentStruct students[NUM_STUDENT];
     8students[4].name = "John Smith";
     9students[4].grade = 100;
    10cout << "The 5th student in the list " << students[4].name;
    11cout << " has a grade of " << students[4].grade;
    12
    13// class example
    14class StudentClass {
    15  double grade;
    16  string name;
    17public:
    18  StudentClass(): name(""), grade(0) {}
    19  StudentClass(string name, double grade): name(name), grade(grade) {}
    20  string getName() { return name;}
    21  double getGrade() { return grade;}
    22};
    23
    24StudentClass students1[NUM_STUDENT];  // default constructor called 100 times
    25students1[4] = StudentClass("John Smith", 100);  // disposable object assignment
    26cout << "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

    1. use assignment or setters:

      1// if name and grade fields are public
      2students[4].name = "John Smith";
      3students[4].grade = 100;
      4
      5// if name and grade fields are accessible only through setters
      6students1[4].setName("John Smith");
      7students1[4].setGrade(100);
      
    • fast, modifying existing objects

    • can be very verbose given large number of fields

    1. assign temporary object:

      students1[4] = Student("John Smith", 100);
      
    • overhead of creating and duplicating temporary objects

    • cleaner syntax

  • Dynamic array example:

    1StudentClass *students2;
    2students2 = new StudentClass[NUM_STUDENT];
    3students1[4] = StudentClass("John Smith", 100);  // temporary object assignment
    4cout << "The 5th student in the list " << students1[4].getName() << " has a grade of " << students1[4].getGrade();
    5delete [] 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:

     1StudentClass *students3[NUM_STUDENT];  // 100 pointers
     2for (int i = 0; i < NUM_STUDENT; ++i)
     3  students3[i] = nullptr;
     4students3[4] = new StudentClass("John Smith", 100);
     5cout << "The 5th student in the list " << students3[4]->getName();
     6cout << " has a grade of " << students3[4]->getGrade();
     7
     8// release memory
     9for (int i = 0; i < NUM_STUDENT; ++i)
    10  delete students3[i];  // delete objects, no []
    
  • Dynamic array example:

     1StudentClass **students4;  // do not confuse with 2D array of objects
     2students4 = new StudentClass *[NUM_STUDENT];  // 100 pointers
     3for (int i = 0; i < NUM_STUDENT; ++i)
     4  students4[i] = nullptr;
     5students4[4] = new StudentClass("John Smith", 100);
     6cout << "The 5th student in the list " << students4[4]->getName();
     7cout << " has a grade of " << students4[4]->getGrade();
     8
     9// release memory
    10for (int i = 0; i < NUM_STUDENT; ++i)
    11  delete students4[i];  // delete objects, no []
    12delete [] students4;
    
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;
}

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