“Smoke and Mirrors”
Part 1: The Nothing Class
Here’s a ‘useful’ class:
//
// Nothing.h
// Assignment 3: Smoke & Mirrors
//
// Created by Bryan Higgs on 10/31/24.
//
#ifndef Nothing_h
#define Nothing_h
class Nothing
{
};
#endif /* Nothing_h */… and here is a similarly useful program that uses it:
//
// TestNothing.cpp
// Assignment 3: Smoke & Mirrors
//
// Created by Bryan Higgs on 10/31/24.
//
#include "Nothing.h"
int main(int argc, const char * argv[])
{
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.
Compile and link the TestNothing.cpp file against your version of the Nothing class, and show that your member functions get called at the right places.
Part 2: An Expedition into Efficiency
Consider the following simple MyString class:
//
// MyString.h
// Assignment 3: An Expedition into Efficiency
//
// Created by Bryan Higgs on 10/31/24.
//
#ifndef MyString_h
#define MyString_h
//
// mystring.h (Not to be confused with string.h!)
//
// A minimal string class
//
#include <string> // for strlen(), strcpy()
#include <iostream> // 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 std::ostream &operator<<(std::ostream &o,
const MyString &src)
{ return o << src.m_ptr; }
private:
size_t m_len; // length of string, in chars
char* m_ptr; // pointer to allocated array of chars
};
#endif /* MyString_h */Now consider the simple Person class:
//
// Person.h
// Assignment 3: An Expedition into Efficiency
//
// Created by Bryan Higgs on 10/31/24.
//
#ifndef Person_h
#define Person_h
//
// A very simple Person class.
//
#include <iostream>
#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()
{
std::cout << "Name: " << m_name << std::endl
<< "Address: " << m_address << std::endl
<< "Age: " << m_age << std::endl;
}
private:
MyString m_name; // Person's name
MyString m_address; // Person's address
int m_age; // Person's age
};
#endif /* Person_h */… and the simple Student class:
//
// Student.h
// Assignment 3: An Expedition into Efficiency
//
// Created by Bryan Higgs on 10/31/24.
//
#ifndef Student_h
#define Student_h
//
// A very simple Student class.
//
#include <iostream>
#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();
std::cout << "Attending: " << m_schoolName << std::endl
<< " " << m_schoolAddress << std::endl;
}
private:
MyString m_schoolName;
MyString m_schoolAddress;
};
#endif /* Student_h */Finally, consider the following main program:
//
// TestStudent.cpp
// Assignment 3: An Expedition into Efficiency
//
// Created by Bryan Higgs on 10/31/24.
//
#include <iostream>
#include "Student.h"
static Student getOlderStudent(Student s1, Student s2)
{
return s1.IsOlder(s2) ? s1 : s2;
}
int main(int argc, const char * argv[])
{
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);
std::cout << "The Older student is:" << std::endl;
s3.Print();
return 0;
}In this program:
- How many times (total), when and why, are the
Studentconstructors (which ones?) and destructor invoked? - How many times (total), when and why, are the
Personconstructors (which ones?) and destructor invoked? - How many times (total), when and why, are each of the
MyStringconstructors (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!