Answers
Day 15
Quiz
1. How do you establish an is-a relationship?
With public inheritance.
2. How do you establish a has-a relationship?
With containment; that is, one class has a member that is an object of another type.
3. What is the difference between containment and delegation?
Containment describes the idea of one class having a data member that is an object of another type. Delegation expresses the idea that one class uses another class to accomplish a task or goal. Delegation is usually accomplished by containment.
4. What is the difference between delegation and implemented-in-terms-of?
Delegation expresses the idea that one class uses another class to accomplish a task or goal. Implemented-in-terms-of expresses the idea of inheriting implementation from another class.
A friend function is a function declared to have access to the protected and private members of your
class.
6. What is a friend class?
A friend class is a class declared so that all its member functions are friend functions of your class.
7. If Dog is a friend of Boy, is Boy a friend of Dog?
No, friendship is not commutative.
8. If Dog is a friend of Boy, and Terrier derives from Dog, is Terrier a friend of
Boy?
No, friendship is not inherited.
9. If Dog is a friend of Boy and Boy is a friend of House, is Dog a friend of House?
No, friendship is not associative.
10. Where must the declaration of a friend function appear?
Anywhere within the class declaration. It makes no difference whether you put the declaration within the public:, protected:, or private: access areas.
Exercises
1. Show the declaration of a class Animal that contains a data member that is a String object.
class Animal:
{
private:
String itsName;
};
2. Show the declaration of a class BoundedArray that is an array.
class boundedArray : public Array
{
//...
}
class Set : private Array
{
// ...
}
Modify Listing 15.1 to provide the String class with an extraction operator (>>).
#include <iostream.h>
#include <string.h>
3:
class String
{
6:
|
public:
| |
7:
|
// constructors
| |
8:
|
String();
| |
9:
|
String(const char *const);
| |
10:
|
String(const String &);
| |
11:
|
~String();
| |
12:
| ||
13:
|
// overloaded operators
| |
14:
|
char & operator[](int offset);
| |
15:
|
char operator[](int offset) const;
| |
16:
|
String operator+(const String&);
| |
17:
|
void operator+=(const String&);
| |
18:
|
String & operator= (const String &);
| |
19:
|
friend ostream& operator<<
| |
20:
|
( ostream&
|
_theStream,String& theString);
|
21:
|
friend istream& operator>>
| |
22:
|
( istream& _theStream,String& theString);
| |
23:
|
// General accessors
| |
24:
|
int GetLen()const { return itsLen; }
| |
25:
|
const char * GetString() const { return itsString;
| |
}
| ||
26:
|
// static int ConstructorCount;
| |
27:
| ||
28:
|
private:
| |
29:
|
String (int);
|
// private constructor
|
30:
|
char * itsString;
| |
31:
|
unsigned short itsLen;
| |
32:
| ||
33
|
};
| |
34:
| ||
35:
|
ostream& operator<<( ostream& theStream,String&
|
{
37: theStream << theString.GetString(); 38: return theStream;
39: }
40:
istream& operator>>( istream& theStream,String&
theString)
{
43: theStream >> theString.GetString();
44: return theStream;
45: } 46:
int main()
{
49: String theString("Hello world.");
50: cout << theString;
return 0;
}
BUG BUSTERS: What is wrong with this program?
1: #include <iostream.h>
2:
3: class Animal;
4:
5: void setValue(Animal& , int);
6:
7:
class Animal
{
public:
int GetWeight()const { return itsWeight; }
int GetAge() const { return itsAge; }
private:
int itsWeight;
int itsAge;
};
17:
void setValue(Animal& theAnimal, int theWeight)
{
friend class Animal;
theAnimal.itsWeight = theWeight;
}
23:
24: int main()
Animal peppy;
setValue(peppy,5);
return 0;
}
You can't put the friend declaration into the function. You must declare the function to be a friend in the class.
Fix the listing in Exercise 5 so that it will compile.
#include <iostream.h>
2:
3: class Animal; 4:
5: void setValue(Animal& , int);
6:
7:
class Animal
{
public:
friend void setValue(Animal&, int);
int GetWeight()const { return itsWeight; }
int GetAge() const { return itsAge; }
private:
int itsWeight;
int itsAge;
};
18:
void setValue(Animal& theAnimal, int theWeight)
{
theAnimal.itsWeight = theWeight;
}
23:
int main()
{
Animal peppy;
setValue(peppy,5);
return 0;
}
BUG BUSTERS: What is wrong with this code?
1: #include <iostream.h>
2:
class Animal;
void setValue(Animal& , int);
void setValue(Animal& ,int,int);
class Animal
{
friend void setValue(Animal& ,int); // here's the change!
private:
int itsWeight;
int itsAge;
};
15:
void setValue(Animal& theAnimal, int theWeight)
{
theAnimal.itsWeight = theWeight;
}
20:
21:
void setValue(Animal& theAnimal, int theWeight, int theAge)
{
theAnimal.itsWeight = theWeight;
theAnimal.itsAge = theAge;
}
27:
int main()
{
Animal peppy;
setValue(peppy,5);
setValue(peppy,7,9);
return 0;
}
The function setValue(Animal&,int) was declared to be a friend, but the overloaded function setValue(Animal&,int,int) was not declared to be a friend.
Fix Exercise 7 so it compiles.
#include <iostream.h>
3: class Animal; 4:
void setValue(Animal& , int);
void setValue(Animal& ,int,int); // here's the change!
class Animal
{
friend void setValue(Animal& ,int,int);
private:
int itsWeight;
int itsAge;
};
16:
void setValue(Animal& theAnimal, int theWeight)
{
theAnimal.itsWeight = theWeight;
}
21:
22:
void setValue(Animal& theAnimal, int theWeight, int theAge)
{
theAnimal.itsWeight = theWeight;
theAnimal.itsAge = theAge;
}
28:
int main()
{
Animal peppy;
setValue(peppy,5);
setValue(peppy,7,9);
return 0;
}
Day 16
Quiz
1. What is the insertion operator and what does it do?
The insertion operator (<<) is a member operator of the ostream object and is used for writing to the output device.
2. What is the extraction operator and what does it do?
The extraction operator (>>) is a member operator of the istream object and is used for writing to your program's variables.
3. What are the three forms of cin.get() and what are their differences?
The first form of get() is without parameters. This returns the value of the character found,
and will return EOF (end of file) if the end of the file is reached.
The second form of cin.get() takes a character reference as its parameter; that character is
filled with the next character in the input stream. The return value is an iostream object. The third form of cin.get() takes an array, a maximum number of characters to get, and a terminating character. This form of get() fills the array with up to one fewer characters than
the maximum (appending null) unless it reads the terminating character, in which case it immediately
writes a null and leaves the terminating character in the buffer.
4. What is the difference between cin.read() and cin.getline()?
cin.read() is used for reading binary data structures. getline() is used to read from the istream's buffer.
5. What is the default width for ouputting a long integer using the insertion operator?
Wide enough to display the entire number.
6. What is the return value of the insertion operator?
A reference to an istream object.
7. What parameter does the constructor to an ofstream object take?
The filename to be opened.
8. What does the ios::ate argument do?
ios::ate places you at the end of the file, but you can write data anywhere in the file.
Exercises
Write a program that writes to the four standard iostream objects: cin, cout, cerr, and clog.
#include <iostream.h>
int main()
{
int x;
cout << "Enter a number: ";
cin >> x;
cout << "You entered: " << x << endl;
cerr << "Uh oh, this to cerr!" << endl;
clog << "Uh oh, this to clog!" << endl;
return 0;
}
Write a program that prompts the user to enter her full name and then displays it on the screen.
int main()
{
char name[80];
cout << "Enter your full name: ";
cin.getline(name,80);
cout << "\nYou entered: " << name << endl;
return 0;
}
Rewrite Listing 16.9 to do the same thing, but without using putback() or ignore().
// Listing
#include <iostream.h>
int main()
{
6:
|
char ch;
| |
7:
|
cout <<
|
"enter a phrase: ";
|
8:
|
while (
|
cin.get(ch) )
|
9:
|
{
| |
10:
|
switch (ch)
| |
11:
|
{
| |
12:
|
case `!':
| |
13:
|
cout << `$';
| |
14:
|
break;
| |
15:
|
case `#':
| |
16:
|
break;
| |
17:
|
default:
| |
18:
|
cout << ch;
| |
19:
|
break;
| |
20:
|
}
|
}
return 0;
}
Write a program that takes a filename as a parameter and opens the file for reading. Read every character of the file and display only the letters and punctuation to the screen. (Ignore all non-printing characters.) Then close the file and exit.
#include <fstream.h>
enum BOOL { FALSE, TRUE };
4: int main(int argc, char**argv) // returns 1 on error
{
if (argc != 2)
{
9:
|
cout << "Usage: argv[0] <infile>\n";
|
10:
|
return(1);
|
11:
|
}
|
12:
|
// open the input stream
ifstream fin (argv[1],ios::binary);
if (!fin)
{
17:
|
cout << "Unable to open " << argv[1] << " for
|
reading.\n";
| |
18:
|
return(1);
|
19:
|
}
|
20:
|
char ch;
while ( fin.get(ch))
23:
|
if ((ch
|
> 32 && ch < 127) || ch == `\n' || ch == `\t')
|
24:
|
cout
|
<< ch;
|
fin.close();
}
Write a program that displays its command-line arguments in reverse order and does not display the program name.
1:
|
#include <fstream.h>
| |
2:
| ||
3:
|
int main(int argc, char**argv)
|
// returns 1 on error
|
{
for (int ctr = argc; ctr ; ctr--)
6: cout << argv[ctr] << " ";
}
Day 17
Quiz
1. What is an inclusion guard?
Inclusion guards are used to protect a header file from being included into a program more than once.
2. How do you instruct your compiler to print the contents of the intermediate file showing the effects of the preprocessor?
3. What is the difference between #define debug 0 and #undef debug?
#define debug 0 defines the term debug to equal 0 (zero). Everywhere the word debug
is found, the character 0 will be substituted. #undef debug removes any definition of debug; when the word debug is found in the file, it will be left unchanged.
4. Name four predefined macros.
__DATE__, __TIME__, __FILE__, __LINE__
5. Why can't you call invariants() as the first line of your constructor?
The job of your constructor is to create the object. The class invariants cannot and should not exist before the object is fully created, so any meaningful use of invariants() will return
false until the constructor is finished.
Exercises
1. Write the inclusion guard statements for the header file STRING.H.
#ifndef STRING_H
#define STRING_H
...
#endif
2. Write an assert() macro that prints an error message and the file and line number if debug level is 2, prints just a message (without file and line number) if the level is 1, and does nothing if the level is 0.
1: #include <iostream.h> 2:
#ifndef DEBUG
#define ASSERT(x)
#elif DEBUG == 1
#define ASSERT(x) \
if (! (x)) \
{ \
9: cout << "ERROR!! Assert " << #x << " failed\n"; \ 10: }
#elif DEBUG == 2
#define ASSERT(x) \
if (! (x) ) \
{ \
cout << "ERROR!! Assert " << #x << " failed\n"; \
cout << " in file " << __FILE__ << "\n"; \
}
#endif
Write a macro DPrint that tests whether debug is defined, and if it is, prints the value passed in as a parameter.
#ifndef DEBUG
#define DPRINT(string) #else
#define DPRINT(STRING) cout << #STRING ;
#endif
4. Write a function that prints an error message. The function should print the line number and filename where the error occurred. Note that the line number and filename are passed in to this function.
1: #include <iostream.h>
2:
void ErrorFunc(
4: int LineNumber,
5: const char * FileName)
{
7: cout << "An error occurred in file ";
8: cout << FileName; 9: cout << " at line "
10: cout << LineNumber << endl;
}
How would you call the preceding error function?
// driver program to exercise ErrorFunc
int main()
{
4: cout << "An error occurs on next line!";
5: ErrorFunc(__LINE__, __FILE__);
return 0;
}
Note that the __LINE__ and __FILE__ macros are used at the point of the error, and not in the error function. If you used them in the error function, they would report the line and file for the error function itself.
6. Write an assert() macro that uses the error function from Exercise 4, and write a driver
1: #include <iostream.h> 2:
3: #define DEBUG // turn error handling on
4:
#ifndef DEBUG
#define ASSERT(x)
#else
#define ASSERT(X) \
9:
|
if (! (X)) \
|
10:
|
{ \
|
11:
|
ErrorFunc(__LINE__, __FILE__); \
|
12:
|
}
|
13:
|
#endif
|
14:
|
void ErrorFunc(int LineNumber, const char * FileName)
{
17: cout << "An error occurred in file ";
18: cout << FileName; 19: cout << " at line ";
20: cout << LineNumber << endl;
21: }
22:
// driver program to exercise ErrorFunc
int main()
{
26:
|
int x = 5;
|
27:
|
ASSERT(x >= 5); // no error
|
28:
|
x = 3;
|
29:
|
ASSERT(x >= 5); // error!
|
return 0;
}
Note that in this case, the __LINE__ and __FILE__ macros can be called in the assert() macro and will still give the correct line (line 29). This is because the assert() macro is
expanded in place, where it is called. Therefore, this program is evaluated exactly as if main() were written as
// driver program to exercise ErrorFunc
int main()
{
4:
|
int x = 5;
| |
5:
|
if (!
|
(x >= 5)) {ErrorFunc(__LINE__, __FILE__);}
|
6:
|
x = 3;
| |
7:
|
if (!
|
(x >= 5)) {ErrorFunc(__LINE__, __FILE__);}
|
}
Day 18
Quiz
1. What is the difference between object-oriented programming and procedural programming?
Procedural programming focuses on functions separate from data. Object-oriented programming ties data and functionality together into objects, and focuses on the interaction among the objects.
2. To what does "event-driven" refer?
Event-driven programs are distinguished by the fact that action is taken only in response to some form of (usually external) simulation, such as a user's keyboard or mouse input.
3. What are the stages in the development cycle?
Typically, the development cycle includes analysis, design, coding, testing, programming, and interaction and feedback among these stages.
4. What is a rooted hierarchy?
A rooted hierarchy is one in which all the classes in the program derive directly or indirectly from a single base class.
5. What is a driver program?
A driver program is simply a function that is designed to exercise whatever objects and functions you are currently programming.
6. What is encapsulation?
Encapsulation refers to the (desirable) trait of bringing together in one class all the data and functionality of one discrete entity.
Exercises
1. Suppose you had to simulate the intersection of Massachusetts Avenue and Vassar Street-- two typical two-lane roads with traffic lights and crosswalks. The purpose of the simulation is to determine whether the timing of the traffic signal allows for a smooth flow of traffic.
What kinds of objects should be modeled in the simulation? What should be the classes defined for the simulation?
Cars, motorcycles, trucks, bicycles, pedestrians, and emergency vehicles all use the intersection. In addition, there is a traffic signal with Walk/Don't Walk lights.
Should the road surface be included in the simulation?
Certainly, road quality can have an effect on the traffic, but for a first design, it may be simpler to leave this consideration aside. The first object is probably the intersection itself. Perhaps the intersection object maintains lists of cars waiting to pass through the signal in each direction, as well as lists of people waiting to cross at the crosswalks. It will need methods to choose which and how many cars and people go through the intersection.
There will be only one intersection, so you may want to consider how you will ensure that only one object is instantiated (hint: think about static methods and protected access).
People and cars are both clients of the intersection. They share a number of characteristics: they can appear at any time, there can be any number of them, and they both wait at the signal (although in different lines). This suggests that you will want to consider a common base class for pedestrians and cars.
The classes would therefore include
class Entity; // a client of the intersection
the root of all cars, trucks, bicycles and emergency vehicles. class Vehicle : Entity ...;
the root of all People
class Pedestrian : Entity...;
class Car :
|
public Vehicle...
|
;
| |||
class Truck
|
: public Vehicle...
|
;
| |||
class Motorcycle : public Vehicle
|
...;
| ||||
class
|
Bicycle : public Vehicle...
|
;
| |||
class
|
Emergency_Vehicle : public
|
Vehicle...
|
;
| ||
// contains lists of cars and people waiting to pass
class Intersection;
2. Suppose the intersections from Exercise 1 were in a suburb of Boston, which has arguably the unfriendliest streets in the United States. At any time, there are three kinds of Boston drivers:
Locals, who continue to drive through intersections after the light turns red
Tourists, who drive slowly and cautiously (in a rental car, typically)
Taxis, which have a wide variation of driving patterns, depending on the kinds of passengers in the cabs
Also, Boston has two kinds of pedestrians:
Locals, who cross the street whenever they feel like it and seldom use the crosswalk buttons
Tourists, who always use the crosswalk buttons and only cross when the Walk/Don't Walk light permits.
Finally, Boston has bicyclists who never pay attention to stoplights
How do these considerations change the model?
A reasonable start on this would be to create derived objects that model the refinements suggested by the problem:
class Local_Car : public Car...;
class Tourist_Car : public Car...; class Taxi : public Car...;
class Local_Pedestrian : public
Pedestrian...;
class Tourist_Pedestrian : public
Pedestrian...;
class Boston_Bicycle : public Bicycle...;
By using virtual methods, each class can modify the generic behavior to meet its own specifications. For example, the Boston driver can react to a red light differently than a tourist does, while still inheriting the generic behaviors that continue to apply.
3. You are asked to design a group scheduler. The software allows you to arrange meetings among individuals or groups, and to reserve a limited number of conference rooms. Identify the principal subsystems.
Two discrete programs need to be written for this project: the client, which the users run; and the server, which would run on a separate machine. In addition, the client machine would have an administrative component to enable a system administrator to add new people and rooms.
If you decide to implement this as a client/server model, the client would accept input from users and generate a request to the server. The server would service the request and send back the results to the client. With this model, many people can schedule meetings at the same time.
On the client's side, there are two major subsystems in addition to the administrative module: the user interface and the communications subsystem. The server's side consists of three main subsystems: communications, scheduling, and a mail interface that would announce to the user when changes have occurred in the schedule.
4. Design and show the interfaces to the classes in the room-reservation portion of the program discussed in Exercise 3.
A meeting is defined as a group of people reserving a room for a certain amount of time. The person making the schedule might want a specific room, or a specified time; but the scheduler must always be told how long the meeting will last and who is required to attend.
The objects will probably include the users of the system as well as the conference rooms. Don't forget to include classes for the calendar, and perhaps a Meeting class that
encapsulates all that is known about a particular event.
The prototypes for the classes might include:
class Calendar_Class;
|
// forward reference
| |
class
|
Meeting;
|
// forward reference
|
class
|
Configuration
|
{
public:
Configuration();
~Configuration();
Meeting Schedule( ListOfPerson&, Delta Time duration );
Meeting Schedule( ListOfPerson&, Delta Time
duration, Time );
Meeting Schedule( ListOfPerson&, Delta Time
duration, Room );
ListOfPerson& People(); // public
accessors
ListOfRoom& Rooms(); // public accessors
protected:
ListOfRoom rooms;
ListOfPerson people;
};
typedef long Room_ID; class Room
{
public:
Room( String name, Room_ID id, int capacity,
String directions = "", String description = "" ); ~Room();
Calendar_Class Calendar();
protected:
Calendar_Class calendar;
int capacity;
Room_ID id;
String name;
directions;
|
// where is
| |
this room?
| ||
String
|
description;
| |
};
| ||
typedef long Person_ID;
| ||
class Person
| ||
{
| ||
public:
| ||
Person( String name, Person_ID id );
| ||
~Person();
| ||
Calendar_Class Calendar();
|
// the access
| |
point to add meetings
| ||
protected:
| ||
Calendar_Class
|
calendar;
| |
Person_ID
|
id;
| |
String
|
name;
| |
};
| ||
class Calendar_Class
| ||
{
| ||
public:
| ||
Calendar_Class();
| ||
~Calendar_Class();
| ||
void Add( const Meeting& );
|
// add a
| |
meeting to the calendar
| ||
void Delete( const Meeting& );
| ||
Meeting* Lookup( Time );
|
// see if
| |
there is a meeting at the
|
// given time
| |
Block( Time, Duration, String reason = "" );
// allocate time to yourself...
protected:
OrderedListOfMeeting meetings;
};
class Meeting
{
public:
Meeting( ListOfPerson&, Room room,
Time when, Duration duration, String purpose
= "" );
~Meeting();
protected:
ListOfPerson people;
Room room;
Time when;
};
Day 19
Quiz
1. What is the difference between a template and a macro?
Templates are built into the C++ language and are type-safe. Macros are implemented by the preprocessor and are not type-safe.
2. What is the difference between the parameter to a template and the parameter to a function?
The parameter to the template creates an instance of the template for each type. If you create six template instances, six different classes or functions are created. The parameters to the function change the behavior or data of the function, but only one function is created.
3. What is the difference between a type-specific template friend class and a general template friend class?
The general template friend function creates one function for every type of the parameterized class; the type-specific function creates a type-specific instance for each instance of the parameterized class.
4. Is it possible to provide special behavior for one instance of a template but not for other instances?
Yes, create a specialized function for the particular instance. In addition to creating
Array<t>::SomeFunction(), also create Array<int>::SomeFunction() to
change the behavior for integer arrays.
5. How many static variables are created if you put one static member into a template class definition?
One for each instance of the class.
Exercises
1. Create a template based on this List class:
class List
{
private:
List():head(0),tail(0),theCount(0) {} virtual ~List();
void insert( int value ); void append( int value );
int is_present( int value ) const;
int is_empty() const { return head == 0; } int count() const { return theCount; }
private:
class ListCell
{
public:
ListCell(int value, ListCell *cell = 0):val(value),next(cell){}
int val;
ListCell *next;
};
ListCell *head; ListCell *tail; int theCount;
};
Here is one way to implement this template:
template <class Type> class List
{
public:
List():head(0),tail(0),theCount(0) { } virtual ~List();
void insert( Type value ); void append( Type value );
int is_present( Type value ) const;
int is_empty() const { return head == 0; } int count() const { return theCount; }
private:
class ListCell
{
public:
ListCell(Type value, ListCell *cell =
0):val(value),next(cell){}
Type val; ListCell *next;
ListCell *head; ListCell *tail; int theCount;
};
2. Write the implementation for the List class (non-template) version.
void List::insert(int value)
{
ListCell *pt = new ListCell( value, head ); assert (pt != 0);
// this line added to handle tail if ( head == 0 ) tail = pt;
head = pt; theCount++;
}
void List::append( int value )
{
ListCell *pt = new ListCell( value ); if ( head == 0 )
head = pt;
else
tail->next = pt;
tail = pt; theCount++;
}
int List::is_present( int value ) const
{
if ( head == 0 ) return 0;
if ( head->val == value || tail->val == value ) return 1;
ListCell *pt = head->next;
for (; pt != tail; pt = pt->next) if ( pt->val == value )
return 1;
return 0;
}
template <class Type> List<Type>::~List()
{
ListCell *pt = head;
while ( pt )
{
ListCell *tmp = pt; pt = pt->next; delete tmp;
}
head = tail = 0;
}
template <class Type>
void List<Type>::insert(Type value)
{
ListCell *pt = new ListCell( value, head ); assert (pt != 0);
// this line added to handle tail if ( head == 0 ) tail = pt;
head = pt; theCount++;
}
template <class Type>
void List<Type>::append( Type value )
{
ListCell *pt = new ListCell( value ); if ( head == 0 )
head = pt;
else
tail->next = pt;
tail = pt; theCount++;
}
template <class Type>
int List<Type>::is_present( Type value ) const
{
if ( head == 0 ) return 0;
if ( head->val == value || tail->val == value )
ListCell *pt = head->next;
for (; pt != tail; pt = pt->next) if ( pt->val == value )
return 1;
return 0;
}
4. Declare three List objects: a list of strings, a list of Cats and a list of ints.
List<String> string_list;
List<Cat> Cat_List;
List<int> int_List;
5. BUG BUSTERS: What is wrong with the following code? (Assume the List template is defined, and Cat is the class defined earlier in the book.)
List<Cat> Cat_List;
Cat Felix; CatList.append( Felix ); cout << "Felix is " <<
( Cat_List.is_present( Felix ) ) ? "" : "not " << "present\n";
Hint: (this is tough) What makes Cat different from int?
Cat doesn't have operator == defined; all operations that compare the values in the List cells, such as is_present, will result in compiler errors. To reduce the chance of this, put
copious comments before the template definition stating what operations must be defined for the instantiation to compile.
6. Declare friend operator == for List.
friend int operator==( const Type& lhs, const Type& rhs );
7. Implement friend operator == for List.
template <class Type>
int List<Type>::operator==( const Type& lhs, const Type& rhs )
{
// compare lengths first
if ( lhs.theCount != rhs.theCount ) return 0; // lengths differ
ListCell *lh = lhs.head;
for(; lh != 0; lh = lh.next, rh = rh.next ) if ( lh.value != rh.value )
return 0;
return 1; // if they don't differ, they must match
}
8. Does operator== have the same problem as in Exercise 4?
Yes, because comparing the array involves comparing the elements, operator!= must be defined for the elements as well.
Implement a template function for swap, which exchanges two variables.
template swap:
must have assignment and the copy constructor defined for the
Type.
template <class Type>
void swap( Type& lhs, Type& rhs)
{
Type temp( lhs ); lhs = rhs;
rhs = temp;
}
Day 20
Quiz
1. What is an exception?
An exception is an object that is created as a result of invoking the keyword throw. It is used to signal an exceptional condition, and is passed up the call stack to the first catch statement that handles its type.
2. What is a try block?
A try block is a set of statements that might generate an exception.
3. What is a catch statement?
A catch statement has a signature of the type of exception it handles. It follows a try block and acts as the receiver of exceptions raised within the try block.
An exception is an object and can contain any information that can be defined within a user-created class.
5. When are exception objects created?
Exception objects are created when you invoke the keyword throw.
6. Should you pass exceptions by value or by reference?
In general, exceptions should be passed by reference. If you don't intend to modify the contents of the exception object, you should pass a const reference.
7. Will a catch statement catch a derived exception if it is looking for the base class?
Yes, if you pass the exception by reference.
8. If there are two catch statements, one for base and one for derived, which should come first?
catch statements are examined in the order they appear in the source code. The first catch statement whose signature matches the exception is used.
9. What does catch(...) mean?
catch(...) will catch any exception of any type.
10. What is a breakpoint?
A breakpoint is a place in the code where the debugger will stop execution.
Exercises
1. Create a try block, a catch statement, and a simple exception.
#include <iostream.h> class OutOfMemory {}; int main()
{
try
{
int *myInt = new int; if (myInt == 0)
throw OutOfMemory();
}
catch (OutOfMemory)
cout << "Unable to allocate memory!\n";
}
return 0;
}
2. Modify the answer from Exercise 1, put data into the exception along with an accessor function, and use it in the catch block.
#include <iostream.h>
#include <stdio.h>
#include <string.h> class OutOfMemory
{
public:
OutOfMemory(char *);
char* GetString() { return itsString; } private:
char* itsString;
};
OutOfMemory::OutOfMemory(char * theType)
{
itsString = new char[80];
char warning[] = "Out Of Memory! Can't allocate room for: "; strncpy(itsString,warning,60); strncat(itsString,theType,19);
}
int main()
{
try
{
int *myInt = new int; if (myInt == 0)
throw OutOfMemory("int");
}
catch (OutOfMemory& theException)
{
cout << theException.GetString();
}
return 0;
}
3. Modify the class from Exercise 2 to be a hierarchy of exceptions. Modify the catch block
1: #include <iostream.h> 2:
// Abstract exception data type
class Exception
{
public:
Exception(){}
virtual ~Exception(){}
virtual void PrintError() = 0;
};
11:
// Derived class to handle memory problems.
// Note no allocation of memory in this class!
class OutOfMemory : public Exception
{
public:
OutOfMemory(){}
~OutOfMemory(){}
virtual void PrintError();
private:
};
22:
void OutOfMemory::PrintError()
{
cout << "Out of Memory!!\n";
}
27:
// Derived class to handle bad numbers
class RangeError : public Exception
{
public:
RangeError(unsigned long number){badNumber = number;}
~RangeError(){}
virtual void PrintError();
virtual unsigned long GetNumber() { return badNumber; }
virtual void SetNumber(unsigned long number) {badNumber =
Ânumber;}
private:
unsigned long badNumber;
};
40:
void RangeError::PrintError()
{
cout << "Number out of range. You used " << GetNumber()
Â"!!\n";
| |
44:
|
}
|
45:
| |
46:
|
void MyFunction(); // func. prototype
|
47:
|
int main()
{
try
{
52: MyFunction();
}
// Only one catch required, use virtual functions to do
the
// right thing.
catch (Exception& theException)
{
58: theException.PrintError();
}
return 0;
}
62:
void MyFunction()
{
65:
|
unsigned int *myInt = new unsigned int;
|
66:
|
long testNumber;
|
67:
|
if (myInt == 0)
|
68:
|
throw OutOfMemory();
|
69:
|
cout << "Enter an int: ";
|
70:
|
cin >> testNumber;
|
71:
|
// this weird test should be replaced by a series
|
72:
|
// of tests to complain about bad user input
|
73:
|
if (testNumber > 3768 || testNumber < 0)
|
74:
|
throw RangeError(testNumber);
|
75:
| |
76:
|
*myInt = testNumber;
|
77:
|
cout << "Ok. myInt: " << *myInt;
|
78:
|
delete myInt;
|
}
Modify the program from Exercise 3 to have three levels of function calls.
1: #include <iostream.h>
2:
// Abstract exception data type
class Exception
{
Exception(){}
virtual ~Exception(){}
virtual void PrintError() = 0;
};
11:
// Derived class to handle memory problems.
// Note no allocation of memory in this class!
class OutOfMemory : public Exception
{
public:
OutOfMemory(){}
~OutOfMemory(){}
virtual void PrintError();
private:
};
22:
void OutOfMemory::PrintError()
{
cout << "Out of Memory!!\n";
}
27:
// Derived class to handle bad numbers
class RangeError : public Exception
{
public:
RangeError(unsigned long number){badNumber = number;}
~RangeError(){}
virtual void PrintError();
virtual unsigned long GetNumber() { return badNumber; }
virtual void SetNumber(unsigned long number) {badNumber =
Ânumber;}
private:
unsigned long badNumber;
};
40:
void RangeError::PrintError()
{
cout << "Number out of range. You used " << GetNumber()
<<
|
Â"!!\n";
|
44:
|
}
|
45:
|
// func. prototypes
void MyFunction();
unsigned int * FunctionTwo();
void FunctionThree(unsigned int *);
int main()
{
try
{
55: MyFunction();
}
// Only one catch required, use virtual functions to do
the
// right thing.
catch (Exception& theException)
{
61: theException.PrintError();
}
return 0;
}
65:
unsigned int * FunctionTwo()
{
unsigned int *myInt = new unsigned int;
if (myInt == 0)
throw OutOfMemory();
return myInt;
}
73:
void MyFunction()
{
76:
|
unsigned int *myInt = FunctionTwo();
|
77:
| |
78:
|
FunctionThree(myInt);
|
79:
|
cout << "Ok. myInt: " << *myInt;
|
80:
|
delete myInt;
|
81:
|
}
|
82:
|
void FunctionThree(unsigned int *ptr)
{
85: long testNumber;
86: cout << "Enter an int: "; 87: cin >> testNumber;
88: // this weird test should be replaced by a series
89: // of tests to complain about bad user input 90: if (testNumber > 3768 || testNumber < 0)
91: throw RangeError(testNumber); 92: *ptr = testNumber;
}
#include "stringc.h" // our string class
class xOutOfMemory
{
public:
xOutOfMemory( const String& where ) : location( where ){} ~xOutOfMemory(){}
virtual String where(){ return location }; private:
String location;
}
main()
{
try {
char *var = new char; if ( var == 0 )
throw xOutOfMemory();
}
catch( xOutOfMemory& theException )
{
cout << "Out of memory at " << theException.location() <<
"\n";
}
}
In the process of handling an "out of memory" condition, a string object is created by the constructor of xOutOfMemory. This exception can only be raised when the program is out of
memory, so this allocation must fail.
It is possible that trying to create this string will raise the same exception, creating an infinite loop until the program crashes. If this string is really required, you can allocate the space in a static buffer before beginning the program, and then use it as needed when the exception is thrown.
Day 21
Quiz
1. What is the difference between strcpy() and strncpy()?
strcpy(char* destination, char* source) copies source to destination, and puts a null at the end of destination. destination must be large enough to accommodate source, or strcpy() will simply write past the end of the array.
strncpy(char* destination char* source, int howmany) will write howmany bytes of source to destination, but will not put a terminating null.
2. What does ctime() do?
ctime() takes a time_t variable and returns an ASCII string with the current time. The time_t variable is typically filled by passing its address to time().
3. What is the function to call to turn an ASCII string into a long?
atol()
4. What does the complement operator do?
It flips every bit in a number.
5. What is the difference between OR and exclusive OR?
OR returns TRUE if either or both bits are set; exclusive OR returns TRUE only if one, but not both, is set.
6. What is the difference between & and &&?
& is the bitwise AND operator, and && is the logical AND operator.
7. What is the difference between | and ||?
| is the bitwise OR operator, and || is the logical OR operator.
Exercises
1. Write a program to safely copy the contents of a 20-byte string into a 10-byte string, truncating whatever won't fit.
#include <iostream.h>
#include <string.h>
int main()
{
char bigString[21] = "12345678901234567890";
char smallString[10];
strncpy(smallString,bigString,9);
smallString[9]='\0';
cout << "BigString: " << bigString << endl;
cout << "smallString: " << smallString << endl;
return 0;
Write a program that tells the current date in the form 7/28/94.
#include <iostream.h>
#include <time.h>
3:
int main()
{
time_t currentTime;
struct tm *timeStruct;
time (¤tTime);
timeStruct = localtime(¤tTime);
cout << timeStruct->tm_mon+1 << "/";
cout << timeStruct->tm_mday << "/";
cout << timeStruct->tm_year << " ";
return 0;
}
Write the definition of a class that uses bit fields to store whether the computer is monochrome or color, a PC or Macintosh, a laptop or a desktop, and whether it has a CD-ROM.
#include <iostream.h>
enum Boolean { FALSE = 0, TRUE = 1 };
class Computer
{
public: // types
enum Machine { Mac = 0, PC };
public: // methods
Computer( Boolean color, Boolean laptop, Machine kind, Boolean cdrom )
: Color( color ), Laptop( laptop ), Kind( kind ), CDRom(
cdrom ){}
~Computer(){}
friend ostream& operator<<( ostream& os, const Computer& computer );
private:
Boolean Color : 1;
Boolean Laptop : 1;
Machine Kind : 1;
};
ostream&
operator<<( ostream& os, const Computer& computer )
{
os << "[";
( computer.Color ) ? os << "color" : os << "monochrome"; os << ", ";
( computer.Laptop ) ? os << "laptop" : os << "desktop"; os << ", ";
( computer.Kind ) ? os << "PC" : os << "Mac"; os << ", ";
( computer.CDRom ) ? os << "" : os << "no "; os << "CD-Rom";
os << "]"; return os;
}
int main()
{
Computer pc( TRUE, TRUE, Computer :: PC, TRUE );
cout << pc << `\n'; return 0;
}
4. Write a program that creates a 26-bit mask. Prompt the user to enter a word, and then quickly report on which letters were used in setting and reading the bits (one bit per character). The program should treat upper- and lowercase letters as the same.
#include <ctype.h>
#include <iostream.h>
#include <string.h>
class Bits
{
public:
enum { BITS_PER_INT = 16 }; Bits( int cnt );
virtual ~Bits();
void clear();
void set( int position ); void reset( int position ); int is_set( int position );
private:
unsigned int * bits;
int Ints_Needed;
};
class AlphaBits : private Bits
{
public:
AlphaBits() : Bits( 26 ){} ~AlphaBits(){}
void clear() { Bits::clear(); } void set( char );
void reset( char ); int is_set( char );
};
Bits :: Bits( int cnt ) : count( cnt )
{
Ints_Needed = count / BITS_PER_INT;
if there is a remainder, you need one more member in array if ( 0 != count % BITS_PER_INT )
Ints_Needed++;
create an array of ints to hold all the bits
bits = new unsigned int[ Ints_Needed ];
clear();
}
Bits :: ~Bits()
{
delete [] bits;
}
void Bits :: clear()
{
// clear the bits
for ( int i = 0; i < Ints_Needed; i++ ) bits[ i ] = 0;
}
void Bits :: set( int position )
{
// find the bit to set
int Int_Number = position / BITS_PER_INT; int Bit_Number = position % BITS_PER_INT;
set the bit
bits[ Int_Number ] |= mask;
}
// clear the bit
void Bits :: reset( int position )
{
int Int_Number = position / BITS_PER_INT; int Bit_Number = position % BITS_PER_INT;
unsigned int mask = ~( 1 << Bit_Number );
bits[ Int_Number ] &= mask;
}
int Bits :: is_set( int position )
{
int Int_Number = position / BITS_PER_INT; int Bit_Number = position % BITS_PER_INT;
unsigned int mask = 1 << Bit_Number;
return ( 0 != ( bits[ Int_Number ] & mask ) );
}
void AlphaBits :: set( char s )
{
make sure the requested character is an alphabetic character
if so, force it to lower case, then subtract the ascii
value
of `a' to get its ordinal (where a = 0, b =1) and set that
bit
if ( isalpha( s ) )
Bits :: set( tolower( s ) - `a' );
}
void AlphaBits :: reset( char s )
{
if ( isalpha( s ) )
Bits :: reset( tolower( s ) - `a' );
}
{
if ( isalpha( s ) )
return Bits :: is_set( tolower( s ) - `a' );
else
return 0;
}
int main()
{
AlphaBits letters;
char buffer[512];
for (;;)
{
cout << "\nPlease type a word (0 to quit): "; cin >> buffer;
if (strcmp(buffer,"0") == 0) break;
// set the bits
for ( char *s = buffer; *s; s++ ) letters.set( *s );
// print the results
cout << "The letters used were: "; for ( char c = `a'; c <= `z'; c++ ) if ( letters.is_set( c ) )
cout << c << ` `; cout << `\n';
// clear the bits letters.clear();
}
return 0;
5. Write a program that sorts the command-line parameters. If the user enters SortFunc cat bird fish dog, the program prints bird cat dog fish.
#include <string.h>
#include <iostream.h>
void swap ( char* &s, char* &t )
{
t = temp;
}
int main( int argc, char* argv[] )
{
Since argv[0] is the program name, //we don't want to sort or print it;
we start sorting at element 1 (not 0).
a "Bubble Sort" is used because of the small number of
items.
int i,j;
for ( i = 1; i < argc; i++ )
for ( j = i + 1; j < argc; j++ )
if ( 0 < strcmp( argv[i], argv[j] ) )
swap( argv[i], argv[j] );
for ( i = 1; i < argc; i++ ) cout << argv[i] << ` `;
cout << `\n'; return 0;
}
6. Write a program that adds two numbers without using the addition operator (+), subtraction operator (-), increment (++), or decrement (--). Hint: Use the bit operators!
If you take a look at the addition of two bits, you'll notice the answer will contain two bits: the result bit and the carry bit. Thus, adding 1 and 1 in binary results in 1 with a carry of 1. If we add 101 to 001, here are the results:
101 // 5
001 //1
110 //6
If you add two "set" bits (each is valued as one), the result is that the result bit is 0 but the carry bit is 1. If you add two clear bits, both the result and the carry are 0. If you add two bits with one set and the other clear, the result bit is 1, but the carry bit is 0. Here is a table that summarizes these rules:
lhs
|
rhs
|
|
|
carry
|
result
|
------------
|
+
|
------------------
| ||
0
|
0
|
|
|
0
|
0
|
0
|
1
|
|
|
0
|
1
|
0
|
|
|
0
|
1
| |
1
|
1
|
|
|
1
|
0
|
Examine the logic of the carry bit. If both bits to be added (lhs and rhs) are 0 or either side is 0, the answer is 0. Only if both bits are 1 is the answer 1. This is exactly the same as the AND operator (&).
In the same way, the result is an XOR (^) operation: if either bit is 1 (but not both), the answer is 1; otherwise, the answer is 0.
When you get a carry, it is added to the next most significant (leftmost) bit. This implies either iterating through each bit or recursion.
#include <iostream.h>
unsigned int add( unsigned int lhs, unsigned int rhs )
{
unsigned int result, carry;
while ( 1 )
{
result = lhs ^ rhs; carry = lhs & rhs;
if ( carry == 0 ) break;
lhs = carry << 1; rhs = result;
};
return result;
}
int main()
{
unsigned long a, b; for (;;)
{
cout << "Enter two numbers. (0 0 to stop): "; cin >> a >> b;
if (!a && !b) break;
cout <<a << " + " << b << " = " << add(a,b) << endl;
}
return 0;
}
#include <iostream.h>
unsigned int add( unsigned int lhs, unsigned int rhs )
{
unsigned int carry = lhs & rhs; unsigned int result = lhs ^ rhs;
if ( carry )
return add( result, carry << 1 );
else
return result;
}
int main()
{
unsigned long a, b; for (;;)
{
cout << "Enter two numbers. (0 0 to stop): "; cin >> a >> b;
if (!a && !b) break;
cout <<a << " + " << b << " = " << add(a,b) << endl;
}
return 0;
}
0 comments:
Post a Comment