Section 5 - The Full Galaxy Program
3B.5.1 Discussion of main()
Before we show the entire class method definitions, have a look at the client:
int main() { Galaxy g1("M"), g2("M51", 8.5), *g3, g4(g2); g1.show(); if ( !g1.setName("andromeda") ) cout << "Couldn't set name to 'andromeda'\n"; if ( !g1.setMag(-5 )) cout << "Couldn't set mag to -5\n"; if ( !g1.setMag(3.4) ) cout << "Couldn't set mag to 3.4\n"; g3 = new Galaxy("sombrero", 10.3); g3->show(); delete g3; g3 = new Galaxy(g2); g3->show(); delete g3; g3 = new Galaxy[500]; g3[0].setName( g1.getName() ); g3[0].setMag( g1.getMag() ); g3[1].setName( g2.getName() ); g3[1].setMag( g2.getMag() ); g3[3].setName( g4.getName() ); g3[3].setMag( g4.getMag() ); for (int k = 0; k < 5; k++) g3[k].show(); cout << endl; delete[] g3; // and a few random calls char myString[100], *strPtr_1; const char *strPtr_2; strcpy(myString, g4.getName()); // strPtr_1 = g2.getName(); // compiler won't let you strPtr_2 = g2.getName(); // strPtr_2[3] = 'X'; // compiler wont' let you cout << "\nSome miscellaneous results: " << myString << " " << strPtr_2 << " " << g1.getName() << endl; return 0; }
Here is the output:
This example pulls together some of the concepts we learned recently about dynamic memory, arrays, and copy constructors. In fact, heap memory appears not only within the Galaxy objects (as we saw in the name member), but also at the higher, client, level. We declared a Galaxy pointer, g3, and in several places allocated and de-allocated memory for it. In one place we even used it to control an entire array of Galaxies. This shows that a pointer doesn't care how much data it points to. It always points to the first object in what could be a single element or a thousand element array. Here are a few lines that pertain to g3:
int main() { Galaxy g2("M", 8.5), *g3; g3 = new Galaxy("sombrero", 10.3); g3->show(); delete g3; g3 = new Galaxy(g2); g3->show(); delete g3; g3 = new Galaxy[500]; g3[1].setName( g2.getName() ); delete[] g3; }
Also, notice that by declaring the getName() as returning a const char *, rather than a char *, we are taking an extra precaution. It is true that we have already moved the name data into a static buffer for the return, thus protecting the private heap array name. Still, we want to protect against the client writing beyond the end of this static array, and by disallowing any modification at all to the return type (with const) we handle this.
// and a few random calls char myString[100], *strPtr_1; const char *strPtr_2; strcpy(myString, g4.getName()); // strPtr_1 = g2.getName(); // compiler won't let you strPtr_2 = g2.getName(); // strPtr_2[3] = 'X'; // compiler wont' let you
3B.5.2 Remaining Galaxy Methods / Entire Listing
For ease of copying and pasting into your compiler, I have placed the entire program, class, main and all, into one file. Here it is, with the remaining class definitions. I am very satisfied that you are at a point in your education where you can understand dynamic memory management, because it is so important in high-performance, real-time gaming and 3-D simulation. Before we go on to our next major topic next week (inheritance) take time to digest this example. It will increase your value to any team, employer, university or client with which you are associated.
// turns off strcpy() error in Visual Studio #ifdef _MSC_VER #define _CRT_SECURE_NO_WARNINGS #endif #include <iostream> #include <cstring> using namespace std; class Galaxy { private: static const double MAX_MAG; static const double MIN_MAG; static const int MAX_NAME; char *name; double magnitude; public: Galaxy(const char *nm = "undefined", double mag = 0.); Galaxy(const Galaxy &gxy); ~Galaxy(); bool setName(const char *nm = "undefined"); const char *getName(); bool setMag(double mag = 0); double getMag(); void show(); }; const double Galaxy::MAX_MAG = 30.0; const double Galaxy::MIN_MAG = -3.0; const int Galaxy::MAX_NAME = 50; int main() { Galaxy g1("M"), g2("M51", 8.5), *g3, g4(g2); g1.show(); if ( !g1.setName("andromeda") ) cout << "Couldn't set name to 'andromeda'\n"; if ( !g1.setMag(-5 )) cout << "Couldn't set mag to -5\n"; if ( !g1.setMag(3.4) ) cout << "Couldn't set mag to 3.4\n"; g3 = new Galaxy("sombrero", 10.3); g3->show(); delete g3; g3 = new Galaxy(g2); g3->show(); delete g3; g3 = new Galaxy[500]; g3[0].setName( g1.getName() ); g3[0].setMag( g1.getMag() ); g3[1].setName( g2.getName() ); g3[1].setMag( g2.getMag() ); g3[3].setName( g4.getName() ); g3[3].setMag( g4.getMag() ); for (int k = 0; k < 5; k++) g3[k].show(); cout << endl; delete[] g3; // and a few random calls char myString[100], *strPtr_1; const char *strPtr_2; strcpy(myString, g4.getName()); // strPtr_1 = g2.getName(); // compiler won't let you strPtr_2 = g2.getName(); // strPtr_2[3] = 'X'; // compiler wont' let you cout << "\nSome miscellaneous results: " << myString << " " << strPtr_2 << " " << g1.getName() << endl; return 0; } // Galaxy method definitions ------------------------ Galaxy::Galaxy(const char *nm, double mag) { name = NULL; // one way to avoid testing mutator return values setName(); setMag(); // process user's requests setName(nm); setMag(mag); } // copy constructor Galaxy::Galaxy(const Galaxy &gxy) { name = NULL; // so that setName knows not do delete setName(gxy.name); setMag(gxy.magnitude); } // destructor Galaxy::~Galaxy() { if (name) delete name; } bool Galaxy::setName(const char *nm) { // return if bad argument passed in if (nm == NULL || strlen(nm) < 2 || strlen(nm) > MAX_NAME-1) return false; // if came from constructor name will be null; don't delete // otherwise free up memory before re-allocating if (name) delete name; // allocate CString and copy name name = new char[ strlen(nm) + 1 ]; strcpy(name, nm); return true; } const char *Galaxy::getName() { // must have a static local to persist after return static char buffer[MAX_NAME]; strcpy(buffer, name); return buffer; } bool Galaxy::setMag(double mag) { if (mag < MIN_MAG || mag > MAX_MAG) return false; magnitude = mag; return true; } double Galaxy::getMag() { return magnitude; } void Galaxy::show() { cout << "Galaxy: " << getName() << " Magnitude " << getMag() << endl; } // end Galaxy method definitions ---------------------