f()
has the prototype
void f(std::vector<int>);
From the point of view of the code calling f()
, which of the following
prototypes
void f1(const std::vector<int> &);
void f2(std::vector<int> &);
are equivalent to f()
's prototype? Explain your answer, which should be
given in terms of behavior; that is, which of the two choices behaves most like
f()
?
From the point of view of the calling code, f1()
behaves like f()
.
Both f1()
and f2()
pass their argument as a reference, but only
f1()
passes the argument as a const reference, which prevents the body of
f1()
from making any changes to the argument. f2()
may change the
argument, and because the argument is passed by reference, those changes will
be visible to the f2()
's caller.
f()
passes its argument by value, which means that, even though
f()
's body can change the argument, those changes are not visible to
f()
's caller.
See Koenig and Moo Section 4.1.3 for more details.
Many people got this question wrong because they didn't read the question
carefully. It is true that f2()
is more like f()
because both
function bodies can change the argument vector, but that point of view is with
respect to the body of the called function. The question asked about behavior
with respect to the calling function, and from that point of view the important
characteristic is that the actual parameter can't change.
try { throw X; }
/* catch clauses here */
Given the class declarations
class blob { /* whatever */ }; class blue_blob : blob { /* whatever */ }; class light_blue_blob : blue_blob { /* whatever */ };
Write the missing catch clauses to provide the following behavior when the try-catch statement is executed:
X
is a blue-blob, the try-catch statement prints Caught a blob.
X
is a light-blue-blob, the try-catch statement prints
Caught a light_blue_blob.
Your catch clauses should be of the form
catch (X) { std::cout << "Caught a X.\n"; }
Don't forget to explain why your answer's correct.
A light-blue blob is at the bottom of the inheritance hierarchy. If it's going to be explicitly caught - as it needs to be to print the light-blue blob message - its catch clause must come before any of the other clauses.
Because a blue blob is a child of a blob, a blob catch clause will catch a thrown blue blob, resulting in the required blob message.
The resulting try-catch clause is
try { throw X; } catch (light_blue_blob) { std::cout << "Caught a light_blue_blob.\n"; } catch (blob) { std::cout << "Caught a blob.\n"; }
A number of people mis-interpreted this question as asking for a separate try-catch statement for each of conditions 1 and 2. Apart from the fact that the question starts off asking you to add catch clauses to a given try statement, writing one try-catch statement per condition would also make an already easy question even easier. A three or so of the people who got this question right had answers like
catch (light-blue-blob) { ... } catch (blob) { ... } catch (blue-blob) { ... }
This is sloppy programming: because a blue-blob is a child of blob, a blue-blob exception will never make it past the blob catch-clause.
A sequence of associative operators may be parenthesized in any way (or not parenthesized at all) without changing the final value. For example, addition is associative and the following sums are all equal:
a + b + c = (a + b) + c = a + (b + c)
The simplest test case (you must have at least three operands to test for associativity) would be to implement the final two sums above:
move r0 a move r0 b move r1 b move r1 c add r0 r1 add r0 r1 move r1 c move r1 a add ro r1 add ro r1 move d ro move d ro
Two or three people got this question right. The most common way to incorrectly answer this question was to present a test case for commutativity.
assert(X); for (int i = v.size(); i >= 0; i -= 2) if (v[i - 1] == v[i - 2]) cnt++;
and what the expression X
should be to explode the assertion before
control reaches the for loop in those cases when the for loop would fail.
The loop can fail when the vector indices are out of bounds. Because i
starts high and is reduced, we have to worry about the left end of the vector.
The first index is out of bounds whenever i - 1
is less than zero i -
1 < 0
or i < 1
. The second index is out of bounds whenever i - 2
is
less than zero i - 2 < 0
or i < 2
.
The indices will be out of bounds when either i < 1
or i < 2
.
Because i < 2
is always true when i < 1
is, we need only check i
< 2
for trouble. If i < 2
is false, that is i >= 2
is true, it
would seem the loop executes without failure. i
gets its initial value
from v.size()
; letting X
represent v.size() >= 2
in the
assertion would appear to keep the loop safe by exploding when v
has less
than two elements.
Unfortunately, the value of i
changes with each loop iteration, and in the
last iteration i
is less than two, violation of the safe access property
i >= 2
derived above. This occurs whenever the loop executes, and because
the loop executes whenever s.size() >= 2
, we have to change our assertion
to explode in those cases by adding the additional test s.size() < 2
to
X
.
The final assertion is
assert((v.size() >= 2) and (v.size() < 2))
But, looking at the assertion, v.size()
can't be both less than and
at least 2 at the same time; this assertion is effectively equal to
assert(false)
That is, the assertion that always explodes. This suggests that something is
wrong with the loop, because it can never execute without exploding. And sure
enough, the loop guard i >= 0
lets the index drop below 2, which we showed
above was a bad idea.
Many answers got the less-than-two part right, but then stopped without
noticing that i
changes value (that is, i
isn't invariant) during the
loop. Loops are hard because they loop; unless you know that the loop does on
every loop, you don't really understand the loop.
This page last modified on 26 February 2003.