[ fromfile: classes-exercises.xml id: classes-exercises ]
Example 2.24, Example 2.25, and Example 2.26 are part of a single program. Use them together for the following problems.
Example 2.24. src/early-examples/thing/thing.h
#ifndef THING_H_ #define THING_H_ class Thing { public: void set(int num, char c); void increment(); void show(); private: int m_Number; char m_Character; }; #endif
<include src="src/early-examples/thing/thing.h" href="src/early-examples/thing/thing.h" id="thingh" mode="cpp"/>
Example 2.25. src/early-examples/thing/thing.cpp
#include <QTextStream> #include "thing.h" void Thing::set(int num, char c) { m_Number = num; m_Character = c; } void Thing::increment() { ++m_Number; ++m_Character; } void Thing::show() { QTextStream cout(stdout); cout << m_Number << '\t' << m_Character << endl; }
<include src="src/early-examples/thing/thing.cpp" href="src/early-examples/thing/thing.cpp" id="thingcpp" mode="cpp"/>
Example 2.26. src/early-examples/thing/thing-demo.cpp
#include <QTextStream> #include "thing.h" void display(Thing t, int n) { int i; for (i = 0; i < n; ++i) t.show(); } int main() { QTextStream cout(stdout); Thing t1, t2; t1.set(23, 'H'); t2.set(1234, 'w'); t1.increment(); //cout << t1.m_Number; display(t1, 3); //cout << i << endl; t2.show(); return 0; }
<include src="src/early-examples/thing/thing-demo.cpp" href="src/early-examples/thing/thing-demo.cpp" id="thingdemocpp" mode="cpp"/>
Uncomment the two commented out lines of code in Example 2.26, and try to build the program using the commands:
qmake -project qmake make
Explain the difference between the errors reported by the compiler.
The first error:
thing.h:10: error: 'int Thing::m_Number' is private thing-demo.cpp:18: error: within this context
is an access error. Thing::m_Number
is visible but not accessible.
The second error:
thing-demo-err.cpp:20: error: 'i' was not declared in this scope
is a visibility error. The variable i
does not exist where it is being invoked.
Add public member functions to the definition of the class Thing
so that the data members can be kept private
, and the client code can still output their values.
Add public "getters" for the two data members.
int getNumber() const { return m_Number; } int getCharacter() const { return m_Character; }
Given the UML diagram in Figure 2.5, define the class, and each member function specified, for an enhanced Fraction
class. You can use Example 2.4 as a starting point.
Write some client code to test all the new operations and verify that proper calculations are done.
Suppose that you want to write an application for a company that matches employers and job seekers. A first step would be to design appropriate classes. Look at Figure 2.6 as a starting point. In this diagram, the Person
has two subobjects: Employer
and Position
.
To do this exercise, you need to use forward class declarations (Section 2.10).
Write classes for Person
, Position
, and Employer
as described in Figure 2.6.
For Person::getPosition()
and getEmployer()
, create and return something funny if the person has not yet been hired by a company.
For the hire(...)
function, set the Person
's state so that future calls to getPosition()
and getEmployer()
give the correct result.
In the main()
program, create at least two Employers, the "StarFleet Federation" and the "Borg."
Create at least two employees, Jean-Luc Picard and Wesley Crusher.
For each class, write a toString()
function that gives you a string
representation of the object.
Write a main program that creates some objects and then prints out each company's list of employees.
Critique the design shown in Figure 2.6. What problems do you see with it? In particular, how would you write Employer::getEmployees()
or Position::getEmployer()
? Suggest how to improve its design.
The design above makes it very difficult to implement these methods because we are copying Employer objects into Person objects. There is no way to maintain identity. We would need pointers or lists of pointers between objects in order to have proper relationships.
Define a class to represent a modern automobile.
Here are some features of this class.
In addition to the four named data members, the constructor should initialize the speed. Zero seems reasonable – it would be awkward to construct a moving automobile.
The drive()
function should be reasonably smart:
It should not permit the car to drive if there is no fuel.
It should adjust the odometer and the fuel amount correctly.
It should return the amount of fuel left in the tank.
The addFuel()
function should adjust the fuel amount correctly and return the resulting amount of fuel in the tank. addFuel(0)
should fill the tank to its capacity.
Write client code to test this class.
The previous problem really did not make use of the speed member. The drive()
function assumed an average speed and used an average fuel consumption rate. Now use the speed member to make things a bit more realistic.
Add a member function to the Hondurota
class that has the prototype
double highwayDrive(double distance, double speedLimit);
The return value is the elapsed time for the trip.
When driving on a highway, it is usually possible to travel at or near the speed limit. Unfortunately, various things happen that can cause traffic to move more slowly, sometimes much more slowly.
Another interesting factor is the effect that changing speed has on the fuel consumption rate. Most modern automobiles have a speed that is optimal for fuel efficiency (e.g., 45 mph). Calculating how long it will take to travel a particular distance on the highway, and how much fuel the trip will consume, is the job of this new function.
Write the function so that it updates the speed, the odometer, and the fuel amount every minute until the given distance has been traveled.
Use 45 mph as the speed at which fuel is consumed precisely at the stored m_FuelConsumptionRate.
Use an adjusted consumption rate for other speeds, increasing the rate of consumption by 1% for each mile per hour that the speed differs from 45mph.
Your car should stop if it runs out of fuel.
Assume that you are traveling at the speed limit, except for random differences that you compute each minute by generating a random speed adjustment between -5 mph and +5 mph. Don't allow your car to drive faster than 40 mph above the speed limit. Of course, your car should not drive slower than 0 mph. If a random speed adjustment produces an unacceptable speed, generate another one.
Write client code to test this function.
Be the computer and predict the output of Example 2.28.
Example 2.27. src/statics/static3.h
#ifndef _STATIC_3_H_ #define _STATIC_3_H_ #include <string> using namespace std; class Client { public: Client(string name) : m_Name(name), m_ID(s_SavedID++) { } static int getSavedID() { if(s_SavedID > m_ID) return s_SavedID; else return 0; } string getName() {return m_Name;} int getID() {return m_ID; } private: string m_Name; int m_ID; static int s_SavedID ; }; #endif
<include src="src/statics/static3.h" href="src/statics/static3.h" mode="cpp"/>
Example 2.28. src/statics/static3.cpp
#include "static3.h" #include <iostream> int Client::s_SavedID(1000); int main() { Client cust1("George"); cout << cust1.getID() << endl; cout << Client::getName() << endl; }
<include src="src/statics/static3.cpp" href="src/statics/static3.cpp" id="static3cpp" mode="cpp"/>
Design and implement a Date
class based on Figure 2.8, subject to the following restrictions and suggestions.
Each Date
must be stored as a single integer equal to the number of days since the fixed base date, January 1, 1000 (or some other date if you prefer). Call that data member m_DaysSinceBaseDate
.
The base year should be stored as a static int
data member (e.g., 1000 or 1900).
The class has a constructor and a set function that have month, day, year parameters. These three values must be used to compute the number of days from the base date to the given date. We have specified a private member function named ymd2dsbd()
to do that calculation for both.
The toString()
function returns a representation of the stored date in some standard string format that is suitable for display (e.g., yyyy/mm/dd). This involves reversing the computation used in the ymd2dsbd()
function described above. We have specified a private member function named getYMD()
to do that calculation. We have also suggested a parameter for the toString()
function (bool brief
) to provide a choice of date formats.
We have specified some static
utility functions (e.g., leapyear()
) that are static
because they do not affect the state of any Date objects.
Make sure you use the correct rule for determining whether a given year is a leap year!
Create a file named date.h
to store your class definition.
Create a file named date.cpp
that contains the definitions of all the functions declared in date.h
.
Write client code to test your Date
class thoroughly.
Your class should handle "invalid" dates in a reasonable way (e.g., year earlier than the base year, month or day out of range, etc.).
Here is the code for setToToday()
that makes use of the system clock to determine today's date. You need to #include <time.h>
(from the C Standard Library) to use this code.
void Date::setToToday() { struct tm *tp = 0; time_t now; now = time(0); tp = localtime(&now); set(1900 + tp->tm_year, 1 + tp->tm_mon, tp->tm_mday); }
getWeekDay()
function returns the name of the week day corresponding to the stored date. Use this in the fancy version of toString()
. Hint: Jan 1, 1900, was a Monday.
Consider the class shown in Example 2.29.
Example 2.29. src/destructor/demo/thing.h
#ifndef THING_H_ #define THING_H_ #include <iostream> #include <string> using namespace std; class Thing { public: Thing(int n) : m_Num(n) { } ~Thing() { cout << "destructor called: " << m_Num << endl; } private: string m_String; int m_Num; }; #endif
<include src="src/destructor/demo/thing.h" href="src/destructor/demo/thing.h" id="thinghh" mode="cpp"/>
The client code in Example 2.30 constructs several objects in various ways and destroys most of them.
Example 2.30. src/destructor/demo/destructor-demo.cpp
#include "thing.h" void function(Thing t) { Thing lt(106); Thing* tp1 = new Thing(107); Thing* tp2 = new Thing(108); delete tp1; } int main() { Thing t1(101), t2(102); Thing* tp1 = new Thing(103); function(t1); { Thing t3(104); Thing* tp = new Thing(105); } delete tp1; return 0; }
<include src="src/destructor/demo/destructor-demo.cpp" href="src/destructor/demo/destructor-demo.cpp" id="destructordemocpp" mode="cpp"/>
Here is the output of this program.
destructor called: 107 destructor called: 106 destructor called: 101 destructor called: 104 destructor called: 103 destructor called: 102 destructor called: 101
How many objects were created but not destroyed?
Two: Numbered 108, and 105.
Why does 101 appear twice in the list?
Because the object t1
was passed by value to function
, copied into temporary t
. Therefore, t
was created and destroyed, and its id was also 101.
Generated: 2012-03-02 | © 2012 Alan Ezust and Paul Ezust. |