Вы находитесь на странице: 1из 9



No: 000

BSc Degree in Computer Science Object-Oriented Programming in C++ (IN2029) January 2009 Answer TWO questions ONLY.

External examiner:

Internal examiners: Ross Paterson Tillman Weyde


a) Dene a class PhoneEntry containing a name and a phone number, both represented as strings. Include a constructor and appropriate selector member functions. (20%) b) Write an appropriate overloading of the == operator for your class. (20%)

c) Dene a Contacts class containing an arbitrary number of PhoneEntry records. Your class should include methods to i) add a new PhoneEntry. ii) given a name, print all the associated phone numbers, if any. iii) given a phone number, print all the associated names, if any. (30%) d) Explain in detail what happens in the following: { Contacts p1; // add some entries to p1; Contacts p2; p2 = p1; Contacts p3 = p1; } (25%) e) What can go wrong when using delete in C++? (5%)

Page 2 of 4


a) Explain how initialization differs from assignment (or not) for each of the following: i) ii) iii) iv) variables of primitive type (e.g. int). objects constants references (20%) b) Suppose a class contains a declaration virtual int capacity() = 0; What sort of declaration is this, what does it mean, and what implications does it have for the use of the class and any derived classes? (15%) c) Write a function that computes the total of a list of doubles. (15%)

d) Write a generic procedure that takes two iterators, assumed to refer to the same container, and returns the distance between them. You may also assume that the position of rst iterator is not after the position of the second iterator. (20%) e) Suppose a legacy windowing library provides the following global functions: int win_create(int width, int height); void win_draw_rectangle(int window_id, int x, int y, int w, int h); void win_draw_text(int window_id, int x, int y, char *str); ... void win_destroy(int window_id); where the win create function returns a numeric identier for the window used by the other functions. The win destroy function removes the window from the screen and releases any resources that it uses. The other functions are used for drawing on the window (before it is destroyed). The last argument to win draw text is a C-style null-terminated character array. Design an idiomatic C++ interface to the above library. Your design should use the resource acquisition is initialization technique to make it easy for clients to ensure that windows are destroyed when no longer in use, even if exceptions occur in other code. Show by example how your interface should be used, and explain how it copes with exceptions. (30%)

Page 3 of 4

3. Suppose a class Person includes the following: Person(const string &n, int a) ... string get_name() const ... int get_age() const ... a) Explain the meanings of the different uses of const in the above class. (10%) b) Dene a derived class Customer of Person, which also has an account balance of type double. Include a constructor and an appropriate selector method. (20%) c) Given the declarations Person p("Alice", 32); Customer c("Bob", 27, 236.75); say whether or not each of the following is legal, and why. For those that are legal, explain what they do. i) ii) iii) iv) c = p; p = c; Person *ptr = &c; Customer *cptr = &p; (25%) d) Consider the following procedure, which is intended to read a series of customer descriptions and store them in a vector: void read_all(vector<Customer *> customers) { string name; int age; double balance; while (cin >> name >> age >> balance) { Customer cust(name, age, balance); customers.push_back(&cust); } } This compiles, but does not work as intended: i) After the method is called, the vector passed as an argument seems unchanged. Explain what is happening and how to x it. (10%) ii) After the above problem is xed, the program crashes on any attempt to access the objects in the vector. Explain what is happening and how to x it, including any implications for the rest of the program. (15%) e) What complications arise when a language has multiple inheritance, and how does C++ deal with these? (20%)

Page 4 of 4

Model Answers and Marking Scheme

1. a) class PhoneEntry { string _name; string _phone; public: PhoneEntry(const string & name, const string & phone) : _name(name), _phone(phone) {} const string & name() const { return _name; } const string & phone() const { return _phone; } }; Marking: members [4], constructor [8] (2 for consts and refs), selectors [8] (2 for consts and refs). b) This could be either a member function bool operator==(const PhoneEntry & other) const { return _name == other._name && _phone == other._phone; } or a global function: bool operator==(const PhoneEntry & p1, const PhoneEntry & p2) { return p1.name() == p2.name() && p1.phone() == p2.phone(); } Marking: procedure head [10, including 1 each for the consts], body [10]. c) class Contacts { vector<PhoneEntry> entries; public: void addEntry(const PhoneEntry & entry) { entries.push_back(entry); } void print_numbers(const string & n) const { for (int i = 0; i < entries.size(); i++) if (entries[i].name() == n) cout << entries[i].phone() << endl; } void print_names(const string & p) const { for (int i = 0; i < entries.size(); i++) if (entries[i].phone() == p) 1

cout << entries[i].name() << endl; } }; No constructor is necessary; the compiler-generated default constructor initializes an empty vector of entries. Iterator loops are acceptable in place of indexing. Any sequence type will do in place of vector, but if they use list, they must use iterators. Marking: class [3], eld [3], add [12], print methods [12]. d) First line initializes p1 using the default constructor of Contacts [3], which initializes an empty vector [3]. Then p2 is initializes using the default constructor like p1 above [3]. Then this is overwritten using the assignment operator of Contacts [3], which assigns the member, destroying the old vector [2] and overwriting it with a copy [2]. The next line initializes p3 using the copy constructor of Contacts [3], which copies the members across, using the copy constructor of vector, which duplicates the vector of phone entries [3]. The closing brace destroys all three [2] in reverse order [1]. e) Possible answers: destroying an object while it is still in use destroying an object twice applying delete to an uninitialized pointer applying delete to a pointer to local or static storage using the wrong form of delete

Marking: 5 marks for two, 3 for one.

Model Answers: page 2



i) For variables of primitive type, there is no difference: both initialization and assignment amount to copying the value. ii) Objects are initialized by constructors. Initialization from another object of the same type invokes the copy constructor. Assignment is done using the assignment operator. When the object holds resources (e.g. storage) that must be freed by a destructor, these are usually dened to do different things: the copy constructor operates on an uninitialized object, while assignment is called for an object that already has these resources, which must often be freed rst. iii) Constants can (indeed must) be initialized, but cannot be assigned to. iv) Initialization of a reference makes it another name for an existing location. Subsequent assignment to the reference assigns to that location.

b) This is a pure virtual member function [3], meaning that the member function has no denition in this class, but may be dened in derived classes [3]. This implies that the class is abstract and cannot be instantiated [3], though it can be used as a type [2]. Subclasses that dene all these member functions may be instantiated [3], and their instances used where the base class is expected [1]. c) int sum(const list<double> & l) { double total = 0; typedef list<double>::iterator Iter; for (Iter p = l.begin(); p != l.end(); ++p) total += *p; return total; } Marking: procedure head [4], iterator loop [5], adding [4], return [2]. template <typename Iterator> int distance(Iterator first, const Iterator & last) { int count; while (first != last) { count++; first++; } return count; } or template <typename Iterator> int distance(Iterator first, const Iterator & last) { int count; for ( ; first != last; first++) count++; return count; } Marking: procedure head [10], iterator loop [5], managing the count [5]. Model Answers: page 3


e) Dene a class class window { int window_id;

public: window(int width, int height) : window_id(create_window(int width, int height)) {} window() { destroy_window(window_id); }

void draw_rectangle(int x, int y, int w, int h) { win_draw_rectangle(window_id, x, y, w, h); void draw_text(int x, int y, const string &s) { win_draw_text(window_id, x, y, s.c_str()); } ... }; Marking: class with constructor saving the window id and destructor using it [10], other functions as member functions of the class [5], proper handling of the string [5]. An example of the intended use [5]: { window w(400, 300); ... w.draw_rectangle(100, 200, 50, 50); w.draw_text(50, 100, "hello"); ... } If control exits the block, whether by exception or by normal completion, the locally allocated window object will be destroyed, invoking its destructor, which will destroy the window on the screen [5].

Model Answers: page 4


a) The parameter n is passed by reference purely for efciency, so it is marked with const to indicate that it wont be changed [5]. The methods annotated with const after the arguments do not alter any of the elds of the object. [5] b) class Customer : public Person { double balance; public: Customer(const string &n, int a, double b) : Person(n, a), balance(b) {} double get_balance() const { return balance; } }; Marking: inheritance [5], private/protected [2] eld [3], constructor [3] initializing parent [2] and eld [2], selector method [3] (including 1 for the const). c) i) Illegal [3], because Person is not a subtype of Customer [1]. ii) Legal [3], because Customer is a subtype of Person [1], but only the Person part of c is copied (c is sliced) [5]. iii) Legal [3], because Customer is a subtype of Person [1], and no objects are copied. Only the address of c is placed in ptr, though only Person attributes may be accessed through this pointer [4]. iv) Illegal [3], because Person is not a subtype of Customer [1]. i) The parameter customers is passed by value, so all updates will be to a copy, not the original [5]. This may be corrected by changing to call-byreference [5]: void read_all(vector<Customer *> &customers) { ii) The vector is lled with pointers to locally allocated objects, which are destroyed soon after [6]. This may be corrected by dynamically allocating the Customer objects [7]: void read_all(vector<Customer *> &customers) { string name; int age; string balance; while (cin >> name >> age >> balance) customers.push_back( new Customer(name, age, balance)); } It will be necessary to delete these Customer objects when they are no longer required [2]. How to refer to a member if two base classes dene the same name? [5] In C++, qualify the name with the appropriate base class. [5] (But no way to override it.) If both base classes are derived from a common base class, should the corresponding data be shared or replicated? [5] In C++, the former is selected by specifying virtual inheritance. [5] Model Answers: page 5