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:

console shot

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 ---------------------