Section 2 - Anonymous Objects
8B.2.1 Objects Without Names

We still want an easy way to create an array of Student objects. We haven't finished defining the Student class yet, but already from what we know about the constructor, we need to supply three pieces of data for each object. And we're trying to do something analogous to what we did with doubles and strings earlier:
double myArray[] = { 10.2, 56.9, -33, 12, 0, 2, 4.8, 199.9, 73, -91.2 }; string myArray[] = { "martin", "claudia", "sandra", "samuels", "terry", "jack", "clark", "palmer", "abraham", "Mike" };
But how can we do this when the base data type of the class we are using in the array is a class? We can create anonymous objects in the initialization. An anonymous object is one that has no variable or identifier associated with it. How could such an object even exist? Easy. Look:
Student myClass[] = { Student("smith","fred", 95), Student("bauer","jack",123), Student("jacobs","carrie", 195), Student("renquist","abe",148), Student("3ackson","trevor", 108), Student("perry","fred",225), Student("loceff","fred", 44), Student("stollings","pamela",452), Student("charters","rodney", 295), Student("cassar","john",321) };
Of course. We don't really need any reference identifiers since the array name and the index, as in myClass[3], are going to be used if we have to identify any of our array elements. So this use of the constructor, in which there is no reference on the LHS of some assignment operator, is perfect.
Incidentally, we can also create anonymous objects if we want to make one just to send off to a method in an argument list, but don't need the object after the call. For example:
drawDot( 100, 40, Color(255, 255, 128) );
This fictitious method call invokes a hypothetical anonymous Color object which we create just to send to the drawDot() method. Perhaps we are drawing a tan colored dot at location 100, 40 on the screen.
We can also use anonymous objects to throw together an object to return in a function:
Student makeGuestStudent() { return Student("Guest", "Student", 0); }
Back to our main topic, though, the initialization of our Student array. We can now see why one of the students ended up with the name "zz-error". Look closely at the data and also the constructor.
8B.2.2 A Support Class for Students - StudentArrayUtilities
We add another component to our design: a class designed to process arrays of Students. This will separate the management of individual students (a Student class responsibility) with that of handling arrays of students (our new StudentArrayUtilities class). This new class has no data, but does have some static methods. Let's look at the class prototype:
// class StudentArrayUtilities prototype ----------------------- class StudentArrayUtilities { public: static void printArray(string title, Student data[], int arraySize); static void arraySort(Student array[], int arraySize); private: static bool floatLargestToTop(Student data[], int top); static void mySwap(Student &a, Student &b); };
We see two public methods:
- printArray() - a method that takes an array of Student objects and sends it out to the screen, and
- arraySort() - a method that takes an array and rearranges it, leaving it in a sorted order.
For our demonstration, we will only do sorts on last name.
8B.2.3 The Client View of the Student and Utility Classes
Now let's look at the entire main() method to see how we are going to have to finish defining Student and StudentArrayUtilities:
int main() { Student myClass[] = { Student("smith","fred", 95), Student("bauer","jack",123), Student("jacobs","carrie", 195), Student("renquist","abe",148), Student("3ackson","trevor", 108), Student("perry","fred",225), Student("loceff","fred", 44), Student("stollings","pamela",452), Student("charters","rodney", 295), Student("cassar","john",321) }; int arraySize = sizeof(myClass) / sizeof(myClass[0]); StudentArrayUtilities::printArray("Before: ", myClass, arraySize); StudentArrayUtilities::arraySort(myClass, arraySize); StudentArrayUtilities::printArray("After: ", myClass, arraySize); }
We're good with the array initialization. Beyond that, there are only three method calls, just like the earlier examples. However, what's interesting about these calls is that they have a class name in front of them. What do you know about method calls dereferenced by class names? I'm waiting ... .
Correct. Static class methods.
And why do we use static class methods, ever? Two reasons, both applicable here:
- They involve data or objects of their own class or a related class, and
- they do not make sense to be called from any individual object.
Great. This tracks. Printing or sorting an array of Students is certainly something that should be done by the Student-related class like StudentArrayUtilities, since such a class would know all the details and pitfalls of the Student data that it supports. At the same time, printing or sorting an array of Students means dealing with a whole group. There is no one single Student object that would naturally be used to dereference this kind of method. Nor do we need an object of StudentArrayUtilities. That's why we are going to declare these as static methods, not as instance methods.
This is so important, I can't say it loud enough. You have to understand why we are going to use a static method, not an instance method, for these two operations. Also, in these cases, we are passing an array of Student objects as an argument to the methods , but in other situations we may not pass any objects of the class into the static methods. It depends on what the static methods are designed to do.
Let's take a peek at another static method (which we have not seen yet), this one in the Student class itself:
bool Student::validString( string testStr ) { if (testStr.length() > 0 && isalpha(testStr[0])) return true; return false; }
Why is this method static? The reason is that it does not act directly on any Student member. The string it is testing for validity is not assocated with a particular member -- it is used by two members firstName and lastName (as we shall see). Its job is to check whether the String that is potentially being assigned to one of those two private members is legal for the class.
8B.2.4 The Full Student Class Definition
Here's another static method of the Student class. This one is going to be used by the utility class's arraySort() method; it establishes the basis for the sort: last name.
// could be an instance method and, if so, would take one parameter int Student::compareTwoStudents( Student firstStud, Student secondStud ) { int result; // this particular version based on last name only (case insensitive) result = firstStud.lastName.compare(secondStud.lastName); return result; }
The comment at the top indicates that it didn't have to be a static method, and if it were designed to be an instance method, instead, it would take only one parameter (why, and what parameter?). However, there is something balanced about seeing both objects to be compared as parameters.
So, we are ready to see the rest of our Student class.
// class Student prototype ----------------------- class Student { private: string lastName; string firstName; int totalPoints; public: static const string DEFAULT_NAME; static const int DEFAULT_POINTS = 0; static const int MAX_POINTS = 1000; public: Student( string lst = DEFAULT_NAME, string fst = DEFAULT_NAME, long pts = DEFAULT_POINTS); // accessors and mutators string getLastName() { return lastName; } string getFirstName() { return firstName; } int getTotalPoints() { return totalPoints; } bool setLastName(string last); bool setFirstName(string first); bool setPoints(int pts); static int compareTwoStudents( Student firstStud, Student secondStud ); string toString(); private: static bool validString( string testStr ); static bool validPoints( int testPoints ); }; // end of class Student prototype -------------- // static initializations that can't be done in-line const string Student::DEFAULT_NAME = "zz-error"; // beginning of Student method definitions ------------- // constructor requires parameters - no default supplied Student::Student( string last, string first, long points) { if ( !setLastName(last) ) lastName = DEFAULT_NAME; if ( !setFirstName(first) ) firstName = DEFAULT_NAME; if ( !setPoints(points) ) totalPoints = DEFAULT_POINTS; } bool Student::setLastName(string last) { if ( !validString(last) ) return false; lastName = last; return true; } bool Student::setFirstName(string first) { if ( !validString(first) ) return false; firstName = first; return true; } bool Student::setPoints(int pts) { if ( !validPoints(pts) ) return false; totalPoints = pts; return true; } // could be an instance method and, if so, would take one parameter int Student::compareTwoStudents( Student firstStud, Student secondStud ) { int result; // this particular version based on last name only (case insensitive) result = firstStud.lastName.compare(secondStud.lastName); return result; } string Student::toString() { string resultString; ostringstream cnvrtFirst, cnvrtLast, cnvrtPoints; cnvrtFirst << firstName; cnvrtLast << lastName; cnvrtPoints << totalPoints; resultString = " "+ cnvrtLast.str() + ", " + cnvrtFirst.str() + " points: " + cnvrtPoints.str() + "\n"; return resultString; } bool Student::validString( string testStr ) { if (testStr.length() > 0 && isalpha(testStr[0])) return true; return false; } bool Student::validPoints( int testPoints ) { if (testPoints >= 0 && testPoints <= MAX_POINTS) return true; return false; } // end of Student method definitions --------------
Study this class first, before going on.
8B.2.5 The Full StudentArrayUtilities Class Definition
// class StudentArrayUtilities prototype ----------------------- class StudentArrayUtilities { public: static void printArray(string title, Student data[], int arraySize); static void arraySort(Student array[], int arraySize); private: static bool floatLargestToTop(Student data[], int top); static void mySwap(Student &a, Student &b); }; // beginning of Student method definitions ------------- // print the array with string as a title for the message box // this is somewhat controversial - we may or may not want an I/O // methods in this class. we'll accept it today void StudentArrayUtilities::printArray(string title, Student data[], int arraySize) { string output = ""; cout << title << endl; // build the output string from the individual Students: for (int k = 0; k < arraySize; k++) output += " "+ data[k].toString(); cout << output << endl; } void StudentArrayUtilities::arraySort(Student array[], int arraySize) { for (int k = 0; k < arraySize; k++) // compare with method def to see where inner loop stops if (!floatLargestToTop(array, arraySize-1-k)) return; } // returns true if a modification was made to the array bool StudentArrayUtilities::floatLargestToTop(Student data[], int top) { bool changed = false; // compare with client call to see where the loop stops for (int k =0; k < top; k++) if ( Student::compareTwoStudents(data[k], data[k+1]) > 0 ) { mySwap(data[k], data[k+1]); changed = true; } return changed; } void StudentArrayUtilities::mySwap(Student &a, Student &b) { Student temp("", "", 0); temp = a; a = b; b = temp; } // end of StudentArrayUtilities method definitions -------------- /* ------------------------------ run ---------------------------- Before: smith, fred points: 95 bauer, jack points: 123 jacobs, carrie points: 195 renquist, abe points: 148 zz-error, trevor points: 108 perry, fred points: 225 loceff, fred points: 44 stollings, pamela points: 452 charters, rodney points: 295 cassar, john points: 321 After: bauer, jack points: 123 cassar, john points: 321 charters, rodney points: 295 jacobs, carrie points: 195 loceff, fred points: 44 perry, fred points: 225 renquist, abe points: 148 smith, fred points: 95 stollings, pamela points: 452 zz-error, trevor points: 108 Press any key to continue . . . ---------------------------------------------------------------- */