> Comp 272 Sample Exam Answers Dordal Oct 16, 2002 > 1. Consider the following operations in a class Date: > > class Date { > public: > Date(int month, int date, int year); > Chi_String tostring(); > void advance(int ndays); // advances the date by a given number of days > }; > > (a). Modify the design of the class so advance returns a new date, > instead of modifying the existing date. Declare the revised version > with all applicable uses of "const". Date advance(int ndays) const; > (b). Suggest a reasonable collection of accessors, that would give you > ways to access and use the date. You should be able to convert the > entire date to a string, and also extract the year, day, and month fields. Chi_String date2str() const; int year() const; int month() const; int day() const; Many variations are possible. > (c). Discuss why it would be a bad idea for the Date class to make any > of its internal fields public. Try to give at least two fundamental > reasons. The reasons apply to all classes: (a) it becomes impossible to change the implementation (b) it becomes impossible to verify class invariants if class users can modify data at will. =============================================================== > 2. (a). Consider a banking system, with classes for checking accounts > and savings accounts. The Checking_Account class has member functions > withdraw(Money amt), deposit(Money amt), and writecheck(Money amt). The > Savings_Account class has member functions withdraw and deposit and > also AddInterest(double interest_rate). > > Arrange these classes in a suitable inheritance hierarchy. Introduce a > common base class with the appropriate operations. Do not write any code! class Account { public: void withdraw(Money amt); void deposit (Money amt); Money balance(); ... } class Checking_Account: public Account { public: void writecheck(Money amt); ... } class Savings_Account: public Account { public: void AddInterest (double interest_rate); ... } =============================================================== > 3. Write a C++ function vsearch(v, x, found) to search a vector v of doubles > for a given value x. If x is found at position v[i], then i is to be returned > and the actual parameter corresponding to found (of type bool) is to be set > to "true"; if i is not found then set found=false. Note that found must be > a reference parameter (declared with &). int vsearch(vector v, int x, bool & found) { // note DECLARATIONS! int i = 0; while (i < v.size() && v[i]!=x) i++; if (i Suppose you need to use a class Foo, which contains a function > Foo::print(). Unfortunately, Foo::print() is not declared to be const, > although it does not change the state of the object. > > You define a class Bar, partially as follows: > class Bar { > private: > int _x; > Foo _f; > public: > Bar(...) {...} > print() const { > cout << _x; > _f.print(); // calls Foo::print() on Foo object _f > } > } > > (a) Why does this fail? Explain. Bar::print() has promised, with const, not to change _x or _f. However, the compiler has no way of knowing, absent a const declaration for Foo::print(), that the call _f.print() within Bar::print() won't change _f. So it is disallowed. > (b). Fix it, *without* removing the const attribute of Bar::print. The normal way to do this would be: Bar(...) {...} print() const { cout << _x; Foo fcopy = _f; // doesn't change _f fcopy.print(); // may change fcopy, but that's ok } =============================================================== > 4. (b). Horstmann's implementation derived AdminMailbox from Mailbox. Yet > the MailSystem::process_dialing function still explicitly checked for > extension 9999 to determine if a mailbox was an Admin box or not (in > other words, no "polymorphism" was used). Given this, what was the > advantage (if any) to using inheritance here? The code is simpler. In the smart-MailSystem version, you have to think if you want to avoid entirely separate code for mailboxes and adminmailboxes. With inheritance, AdminMailbox::do_command(int s) becomes if (s==4) create_mailbox; else Mailbox::do_command(s); In other words, we simply add a fourth option but for the other options we pass the message back to the parent Mailbox class. =============================================================== > 5. Consider the following outline of a list interface. Assume the > list are of objects of type X. Note that this interface maintains > a "current" element of the list; the internal "cursor" is in effect > a pointer to the current element. > > class listX { public: > start() // set an internal "cursor" to start of list > next(); // advance cursor to next element > set(); // set current element to a new value > get(); // return current element > add(); // add new element after current cursor position > addbefore(); // like add, but inserts new element in front of current > delete(); // deletes current element > islast(); // true if current element is last element > length(); // return length of list > ... > } > > (a). Provide parameter lists for all the above, including const where > appropriate (where the actual parameter will not be modified). Also > declare whether the member function itself is const or not. void start(); void next(); void set(X x); X get() const; void add(X x); void addbefore(X x) void delete(); Bool islast() const; int length() const; > (b). Explain why both add() and addbefore() are necessary; that is, > give an example of something that can be done with add() but not > addbefore() and something that can be done with addbefore() but not > with add(). add() cannot add to the front of the list; addbefore() can't add to the tail. =============================================================== > 6. The list class in problem 5 has a disadvantage in that there can be > at most one cursor. Implementing a linked-list class with public > pointer fields is that one way to support multiple cursors (pointers) > into the same list, but the price is that a critical data field > is now public, which ties one to a specific implementation, and causes > a loss of some type safety. > > One way to have the best of both worlds is to implement "external > iterators"; that is, one has a class List and also a class ListCursor. > A ListCursor object represents a pointer into a linked list; > alternatively, if the List is implemented as an array, then a > ListCursor represents an array index value. All list operations that > refer to the "current" element would now be made in terms of a > ListCursor object. The class ListCursor would be able to access > the private: data of class List, either because ListCursor was > declared inside class List, or was declared to be a "friend" class. > > Specify the interface for List and ListCursor, and indicate with a > comment what each operation does. As a hint, a constructor for a > ListCursor is specified; it takes a List reference and sets the > ListCursor to mark the first element of the list. There are multiple ways to do this, none of them perfect. The solution below uses a ListCursor as a parameter to all List operations for which the position in the list is applicable. This problem is waaay too hard to *put* on an exam, but as a review question it seemed plausible. But if it doesn't make sense to you, don't worry about it. class List { friend ListCursor; // "friend" means it can access private data public: int length() const; // in the following, a ListCursor is passed // as a "pointer" to the desired element or position. void set(X x, ListCursor L); X get(ListCursor L) const; void add(X x, ListCursor L); void addbefore(X x, ListCursor L) void delete(ListCursor L); }; class ListCursor { friend List; public: ListCursor (List & L); // Sets the listcursor to point to head of L // takes place of start() // these are the only two that simply deal with position, not data! void next(); Bool islast() const; } =============================================================== > 7. Define a class Complex (for complex numbers). Do *not* implement > any of the operations, though! A complex number should have two > data fields, _real and _imag, of type double. You *should* implement > the following constructors, though: > (a) Complex (double, double); // set _real and _imag > (b) Complex (double) // set _real; let _imag = 0; > (c) default ctor class Complex { private: double _real, _imag; public: Complex() : _real(0.0), _imag(0.0) {} Complex(double r) : _real(r), _imag(0.0) {} // imaginary part is 0 Complex(double r, double i) : _real(r), _imag(i) {} Complex add(Complex z, Complex w); ... // more operations };