|
| |
C++ 'Smoke and Mirrors'
The Nothing class
Here's a 'useful' class:
#ifndef __NOTHING_H
#define __NOTHING_H
//
// nothing.h
//
class Nothing
{
};
#endif
|
and here is a similarly useful program that uses it:
//
// nothing.cpp
//
#include "nothing.h"
int main()
{
const Nothing ness; // Some compilers don't like this
//const Nothing ness = Nothing(); // so try this instead
Nothing doing(ness); // If your compiler doesn't like this
//Nothing doing = ness; // try this instead
doing = ness;
Nothing *here = &doing;
const Nothing *anywhere = &ness;
return 0;
}
|
Convince yourself that this program compiles without
error, and executes without failure.
Explain why. What did the C++ compiler do for you?
Write a Nothing
class that does exactly the same thing as the (empty) one
above, but where you supply all the equivalent member
functions explicitly. Just change nothing.h,
not nothing.cpp.
Compile and link the nothing.cpp
file against your version of the Nothing
class, and show that your member functions get called at the
right places.
An Expedition into Efficiency
Consider the following simple MyString
class:
#ifndef __MYSTRING_H
#define __MYSTRING_H
//
// mystring.h (Not to be confused with string.h!)
//
// A minimal string class
//
#include <string.h> // for strlen(), strcpy()
#include <iostream.h> // for ostream stuff
class MyString
{
public:
MyString()
: m_len(0), m_ptr(new char[1])
{ m_ptr[0] = '\0'; }
MyString(const char *str)
: m_len(strlen(str))
{
m_ptr = new char[m_len+1];
strcpy(m_ptr, str);
}
MyString(const MyString &src)
: m_len(src.m_len), m_ptr(new char[src.m_len+1])
{ strcpy(m_ptr, src.m_ptr); }
~MyString()
{ delete [] m_ptr; /* Clean up */ }
MyString &operator=(const MyString &src)
{
if (this != &src) // Prevent assignment to self, causing corruption
{
delete [] m_ptr; // Clean up previously allocated space first
m_len = src.m_len;
m_ptr = new char[m_len+1];
strcpy(m_ptr, src.m_ptr);
}
return *this;
}
friend ostream &operator<<(ostream &o, const MyString &src)
{ return o << src.m_ptr; }
private:
unsigned int m_len; // length of string, in chars
char *m_ptr; // pointer to allocated array of chars
};
#endif
|
Now consider the simple Person
class:
#ifndef __PERSON_H
#define __PERSON_H
//
// person.h
//
// A very simple Person class.
//
#include <iostream.h>
#include "mystring.h"
class Person
{
public:
Person(const char *n, const char *adr, int ag)
: m_name(n), m_address(adr), m_age(ag)
{}
virtual ~Person() {}
int IsOlder(const Person &other)
{ return m_age > other.m_age; }
void Print()
{
cout << "Name: " << m_name << endl
<< "Address: " << m_address << endl
<< "Age: " << m_age << endl;
}
private:
MyString m_name; // Person's name
MyString m_address; // Person's address
int m_age; // Person's age
};
#endif
|
and the simple Student
class:
#ifndef __STUDENT_H
#define __STUDENT_H
//
// student.h
//
// A very simple Student class.
//
#include <iostream.h>
#include "person.h"
#include "mystring.h"
class Student : public Person
{
public:
Student(const char *nam, const char *adr, int ag,
const char *sch, const char *schadr)
: Person(nam, adr, ag),
m_schoolName(sch), m_schoolAddress(schadr)
{}
virtual ~Student() {}
void Print()
{
Person::Print();
cout << "Attending: " << m_schoolName << endl
<< " " << m_schoolAddress << endl;
}
private:
MyString m_schoolName;
MyString m_schoolAddress;
};
#endif
|
Finally, consider the program:
//
// student.cpp
//
#include <iostream.h>
#include "student.h"
static Student getOlderStudent(Student s1, Student s2)
{
return s1.IsOlder(s2) ? s1 : s2;
}
int main()
{
Student s1("Fred Bloggs", "56 Main St., Anytown, USA", 23,
"Yale", "New Haven, CT");
Student s2("Mary Jones", "12 Canyon Court, Prime Real Estate, USA", 19,
"Harvard", "Cambridge, MA");
Student s3 = getOlderStudent(s1, s2);
cout << "The Older student is:" << endl;
s3.Print();
return 0;
}
|
In this program:
- How many times (total), when and why, are the Student
constructors (which ones?) and destructor invoked?
- How many times (total), when and why, are the Person
constructors (which ones?) and destructor invoked?
- How many times (total), when and why, are each of the
MyString constructors (which ones?) and destructor
invoked?
- How can you improve these figures? (You should be
able to improve these numbers significantly!)
Hint: From the start of the
program to its end, what would you expect the
relationship to be between the number of calls to a class
constructor and the number of calls to the same class's
destructor?
Modify the program in student.cpp (not the classes!)
to significantly reduce the number of member functions that
are invoked, while still producing correct results.
Do not add to or remove
any of the arguments in the getOlderStudent() function!
|