CT 301 Final - Practice Questions for Exam
Practice Questions for Exam
Homework 1 concepts (the inventory system)
Trace each snippet against the HW#1 classes (Item, Equipment/Consumable/Borrowed, Inventory, Date).
Trace this program's output: (select all that apply)
Equipment e("E1","Rig","compute", 1000.0, Date{2026,1,1},
"Dell","XPS","i7", /*obsolete*/false, /*broken*/true,
Date{2027,1,1}, Date{2027,1,1});
std::cout << e.canLoan() << e.canSell() << e.currentValue();
- prints
010: broken ⇒canLoan()is false;canSell()is still the defaulttrue; a broken unit'scurrentValue()is 0 Itemis abstract (canLoan/currentValue/attribute/printToare pure virtual), soItem x{...};would not compile- prints
111 canSell()isfalsebecause the unit is broken
Show answer
Answer: A, B
- prints
010: broken ⇒canLoan()is false;canSell()is still the defaulttrue; a broken unit'scurrentValue()is 0 Itemis abstract (canLoan/currentValue/attribute/printToare pure virtual), soItem x{...};would not compile
operator<< through a base referenceWhat does os contain? (select all that apply)
std::unique_ptr<Item> p =
std::make_unique<Consumable>("C1","Cable","supply", 5.0, 10, "Belkin","M");
std::ostringstream os;
os << *p; // Item& -> which printTo?
osgetsConsumable::printTooutput (dynamic dispatch throughItem&): it containsCONSUMABLEandvalue=50.00- one non-member
operator<<(ostream&, const Item&)serves every item type osgetsItem::printTooutput (there is none —printTois pure virtual)- compile error, because
pis declaredItem, notConsumable
Show answer
Answer: A, B
osgetsConsumable::printTooutput (dynamic dispatch throughItem&): it containsCONSUMABLEandvalue=50.00- one non-member
operator<<(ostream&, const Item&)serves every item type
Borrowed item: it is not oursTrace the output: (select all that apply)
Inventory inv;
inv.add(std::make_unique<Borrowed>("B1","Scope","instr", 999.0,
"Physics", Date{2026,12,1}, "Tek","T1"));
auto rejects = inv.sell(inv.select_all());
std::cout << rejects.size() << " " << inv.revenue();
- prints
1 0: a borrowed item is not ours to sell, so it is rejected and revenue stays 0 sellreturns the items it could not sell- prints
0 999: the item sells for its value - the borrowed item moves to
State::Sold
Show answer
Answer: A, B
- prints
1 0: a borrowed item is not ours to sell, so it is rejected and revenue stays 0 sellreturns the items it could not sell
The code's reference "today" is 2026-06-01. What prints? (select all that apply)
// bought 2021-01-01, base value 1000, not broken
Equipment e("E1","Rig","compute", 1000.0, Date{2021,1,1},
"Dell","T","i9", false, false, Date{2030,1,1}, Date{2030,1,1});
std::cout << e.currentValue();
- prints
100: 5 years at 20%/yr wipes the value past the 10% floor, so it clamps tobaseValue*0.10 - the value never drops below 10% of the base value
- prints
0: 5 × 20% = 100% depreciation - prints
1000: the unit is not broken
Show answer
Answer: A, B
- prints
100: 5 years at 20%/yr wipes the value past the 10% floor, so it clamps tobaseValue*0.10 - the value never drops below 10% of the base value
Consumable: not loanableTrace the output: (select all that apply)
Inventory inv;
inv.add(std::make_unique<Consumable>("C1","Ink","supply", 4.0, 25, "HP","M"));
auto rejects = inv.loan(inv.select_all(), "Alice", Date{2026,6,1});
std::cout << rejects.size();
- prints
1:Consumable::canLoan()isfalse, soloanrejects it loanreturns the items it could not loan- prints
0: the consumable is loaned to Alice - a consumable becomes
OnLoanlike equipment
Show answer
Answer: A, B
- prints
1:Consumable::canLoan()isfalse, soloanrejects it loanreturns the items it could not loan
Trace the two counts printed: (select all that apply)
Inventory inv;
inv.add(std::make_unique<Equipment>("E1","Cam","av", 500.0, Date{2026,1,1},
"Sony","A","x", false, false, Date{2027,1,1}, Date{2027,1,1}));
inv.loan(inv.select_all(), "Bob", Date{2026,6,1});
auto c = inv.count_by_state();
std::cout << c[State::InStock] << c[State::OnLoan];
- prints
01: the equipment moves from in-stock to on-loan - changing state moves the owning
unique_ptrbetween the inventory's per-state vectors - prints
10: loaning does not change the item's state - prints
11: the item is counted in two states at once
Show answer
Answer: A, B
- prints
01: the equipment moves from in-stock to on-loan - changing state moves the owning
unique_ptrbetween the inventory's per-state vectors
One sellable Equipment, one Borrowed. What prints? (select all that apply)
Inventory inv;
inv.add(std::make_unique<Equipment>("E1",...,false,false,...)); // sellable
inv.add(std::make_unique<Borrowed>("B1",...)); // not sellable
auto rej = inv.sell(inv.select_all());
std::cout << rej.size() << " " << inv.count_by_state()[State::Sold];
- prints
1 1: only the equipment sells; the borrowed item comes back in the reject list sellprocesses each item independently and collects the ones it cannot sell- prints
0 2: both items sell - prints
2 0: the whole call fails because one item is not sellable
Show answer
Answer: A, B
- prints
1 1: only the equipment sells; the borrowed item comes back in the reject list sellprocesses each item independently and collects the ones it cannot sell
Date comparisons and zero-padded stream outputTrace the printed characters and os.str(): (select all that apply)
std::cout << (Date{2026,1,5} < Date{2026,2,1})
<< (Date{2026,6,1} < Date{2026,6,1});
std::ostringstream os; os << Date{2026,6,1};
- the two comparisons print
10(Jan 5 is before Feb 1; a date is not<itself) os.str()is"2026-06-01"(zero-paddedYYYY-MM-DD)- the comparisons print
11 os.str()is"2026-6-1"
Show answer
Answer: A, B
- the two comparisons print
10(Jan 5 is before Feb 1; a date is not<itself) os.str()is"2026-06-01"(zero-paddedYYYY-MM-DD)
attribute(key) returns std::optional; unknown key → nulloptTrace the output: (select all that apply)
Equipment e("E1","Rig","c", 1000.0, Date{2026,1,1},
"Dell","XPS","i7", false,false, Date{2027,1,1}, Date{2027,1,1});
auto brand = e.attribute("brand"); // std::optional<std::string>
auto miss = e.attribute("weight"); // not a field
std::cout << brand.value() << " " << miss.has_value();
- prints
Dell 0: a known key returns its value; an unknown key returnsstd::nullopt attributereturnsstd::optional<std::string>, so callers testhas_value()/value()- prints
Dell 1: every key returns a value e.attribute("weight")throws because the key is missing
Show answer
Answer: A, B
- prints
Dell 0: a known key returns its value; an unknown key returnsstd::nullopt attributereturnsstd::optional<std::string>, so callers testhas_value()/value()
unique_ptr member makes the whole class move-onlyWhich lines compile? (select all that apply)
Inventory a;
a.add(std::make_unique<Equipment>("E1", ...));
Inventory b = a; // (1)
Inventory c = std::move(a); // (2)
- line (1) does not compile: copying the inventory would copy a
unique_ptr, whose copy is deleted - line (2) compiles: the inventory is movable (its vectors transfer), following the rule of zero
- both lines compile and
b,care independent deep copies - line (2) also fails to compile, because a
unique_ptrcannot be moved either
Show answer
Answer: A, B
- line (1) does not compile: copying the inventory would copy a
unique_ptr, whose copy is deleted - line (2) compiles: the inventory is movable (its vectors transfer), following the rule of zero
total_value sums the virtual currentValue()One Equipment (value 1000), one Borrowed (base 999). What prints? (select all that apply)
Inventory inv;
inv.add(std::make_unique<Equipment>("E1","R","c", 1000.0, Date{2026,1,1},
"D","T","i", false,false, Date{2027,1,1}, Date{2027,1,1}));
inv.add(std::make_unique<Borrowed>("B1","Scope","i", 999.0,
"Physics", Date{2026,12,1}, "Tek","T1"));
std::cout << inv.total_value(inv.select_all());
- prints
1000:total_valuesums each item's owncurrentValue(); the borrowed item contributes 0 despite its 999 base value - the sum dispatches dynamically to each item type's
currentValue() - prints
1999: it adds the base values (1000 + 999) - the borrowed item must be filtered out by hand to reach 1000
Show answer
Answer: A, B
- prints
1000:total_valuesums each item's owncurrentValue(); the borrowed item contributes 0 despite its 999 base value - the sum dispatches dynamically to each item type's
currentValue()
Homework 2 concepts (the hand-built vector)
Trace each snippet against your ct::Vector and the free-store material from Lectures 7-10.
Trace the output, and say whether it leaks: (select all that apply)
{
ct::Vector<int> v;
for (int i = 0; i < 1000; ++i) v.push_back(i);
std::cout << v.size();
} // <- v goes out of scope here
- prints
1000, thenv's destructor runs at the closing brace and frees the heap buffer (RAII) — no leak, no manualdelete - the destructor destroys the elements, then releases the storage
- the buffer leaks because nothing calls
delete - you must write
delete v;yourself before the closing brace
Show answer
Answer: A, B
- prints
1000, thenv's destructor runs at the closing brace and frees the heap buffer (RAII) — no leak, no manualdelete - the destructor destroys the elements, then releases the storage
Trace the output: (select all that apply)
ct::Vector<std::string> v; v.reserve(10); std::cout << v.size() << " " << (v.capacity() >= 10);
- prints
0 1:reserveobtains raw storage (capacity) without constructing any element - this is why capacity can exceed size —
::operator new(bytes) is separate from placementnew(construction) - prints
10 1:reserve(10)constructs 10 empty strings reserveis the same asresize
Show answer
Answer: A, B
- prints
0 1:reserveobtains raw storage (capacity) without constructing any element - this is why capacity can exceed size —
::operator new(bytes) is separate from placementnew(construction)
On a 64-bit build, what prints? (select all that apply)
int a[4] = {10,20,30,40};
auto f = [](int* p){ return sizeof(p); };
std::cout << sizeof(a) << " " << f(a);
- prints
16 8: insidefthe array has decayed to a pointer, sosizeofis the pointer size - passing
atofdecays it toint*, losing the length - prints
16 16: the array keeps its size when passed int b[4]; b = a;would copy the array
Show answer
Answer: A, B
- prints
16 8: insidefthe array has decayed to a pointer, sosizeofis the pointer size - passing
atofdecays it toint*, losing the length
char[] copy; p[i] == *(p+i)Trace the output: (select all that apply)
char s[] = "hi"; s[0] = 'H'; std::cout << s << " " << (s[1] == *(s + 1));
- prints
Hi 1:char s[]is a writable copy, ands[i]is exactly*(s+i) sis a private, writable copy of the characters, so assignings[0]is finesand the literal"hi"refer to the same bytes- prints
hi 1: you cannot modifys
Show answer
Answer: A, B
- prints
Hi 1:char s[]is a writable copy, ands[i]is exactly*(s+i) sis a private, writable copy of the characters, so assignings[0]is fine
Trace the output: (select all that apply)
ct::Vector<int> a{1,2,3};
ct::Vector<int> b = a; // copy
b[0] = 99;
std::cout << a[0] << " " << (a.data() != b.data());
- prints
1 1: the copy is deep —bhas its own buffer, so changingb[0]leavesauntouched - the copy constructor allocates a new buffer and copies the elements
- prints
99 1:aandbshare the buffer - prints
1 0:aandbpoint at the same storage
Show answer
Answer: A, B
- prints
1 1: the copy is deep —bhas its own buffer, so changingb[0]leavesauntouched - the copy constructor allocates a new buffer and copies the elements
std::move is only a castTrace the output: (select all that apply)
ct::Vector<int> a{1,2,3};
ct::Vector<int> b = std::move(a);
std::cout << b.size() << " " << a.size();
- prints
3 0: the move steals the buffer; the moved-from source is left valid and empty std::move(a)is only a cast to an rvalue reference; the move constructor does the transfer- prints
3 3: move copies the elements, leavingaintact - the moved-from
akeeps{1,2,3}as a backup copy
Show answer
Answer: A, B
- prints
3 0: the move steals the buffer; the moved-from source is left valid and empty std::move(a)is only a cast to an rvalue reference; the move constructor does the transfer
Trace the output: (select all that apply)
ct::Vector v{10, 20, 30}; // no <int>
ct::Vector<double> w(3, 1.5); // count-and-value
std::cout << std::is_same_v<decltype(v), ct::Vector<int>>
<< " " << w.size() << " " << w[0];
- prints
1 3 1.5: CTAD deducesct::Vector<int>from the braces;wis 3 copies of 1.5 - CTAD lets you omit
<int>when the element type is deducible - prints
0 3 1.5:visct::Vector<double> ct::Vector v{10,20,30};fails to compile without<int>
Show answer
Answer: A, B
- prints
1 3 1.5: CTAD deducesct::Vector<int>from the braces;wis 3 copies of 1.5 - CTAD lets you omit
<int>when the element type is deducible
at() throws; operator[] is uncheckedTrace the output: (select all that apply)
ct::Vector<int> v{1,2,3};
try { std::cout << v.at(9); }
catch (const std::out_of_range&) { std::cout << "oor"; }
std::cout << " " << v[0];
- prints
oor 1:at(9)throwsstd::out_of_range;operator[]is unchecked (herev[0]) reserve(n)withn > max_size()throwsstd::length_error(a different exception)- prints
0 1:at(9)returns a defaultint operator[]also throws when the index is out of range
Show answer
Answer: A, B
- prints
oor 1:at(9)throwsstd::out_of_range;operator[]is unchecked (herev[0]) reserve(n)withn > max_size()throwsstd::length_error(a different exception)
Trace the output: (select all that apply)
ct::Vector<int> v; // capacity 0 for (int i = 0; i < 9; ++i) v.push_back(i); std::cout << v.size() << " " << v.capacity();
- prints
9 16: capacity grows 0 → 8 on the first push, then doubles to 16 when the 9th overflows 8 - growth is geometric, so many pushes cause only a few reallocations
- prints
9 9: capacity always equals size - prints
9 32
Show answer
Answer: A, B
- prints
9 16: capacity grows 0 → 8 on the first push, then doubles to 16 when the 9th overflows 8 - growth is geometric, so many pushes cause only a few reallocations
Trace the output: (select all that apply)
ct::Vector<int> a{1,2,3};
ct::Vector<int> c = a; // copy: own buffer
ct::Vector<int> m = std::move(a); // move: steal
std::cout << (c.data() != m.data()) << " " << a.size();
- prints
1 0: the copy has its own buffer; the move stolea's buffer, leavingasize 0 - copy allocates and copies each element; move takes pointer/size/capacity and nulls the source
- prints
0 3:candmshare a buffer andais unchanged - prints
1 3
Show answer
Answer: A, B
- prints
1 0: the copy has its own buffer; the move stolea's buffer, leavingasize 0 - copy allocates and copies each element; move takes pointer/size/capacity and nulls the source
reserve (capacity only) vs resize (element count)Trace the output: (select all that apply)
ct::Vector<int> v; v.reserve(50); std::size_t a = v.size(); v.resize(5); std::cout << a << " " << v.size() << " " << (v.capacity() >= 50);
- prints
0 5 1:reserveraised capacity only (size still 0);resize(5)set the element count to 5 reservenever changes size;resizecreates or destroys elements- prints
50 5 1:reserve(50)makes size 50 - prints
0 5 0:resize(5)shrank capacity below 50
Show answer
Answer: A, B
- prints
0 5 1:reserveraised capacity only (size still 0);resize(5)set the element count to 5 reservenever changes size;resizecreates or destroys elements
rbegin() is the last elementTrace the output: (select all that apply)
ct::Vector<int> v{5,6,7,8};
std::cout << (v.end() - v.begin()) << " "
<< *v.rbegin() << " "
<< std::is_same_v<ct::Vector<int>::iterator, int*>;
- prints
4 8 1:end()-begin()is the size;rbegin()refers to the last element; the iterator isint* - iterators are raw pointers into the contiguous buffer
- prints
4 5 1:rbegin()refers to the first element - prints
3 8 1:end()refers to the last element
Show answer
Answer: A, B
- prints
4 8 1:end()-begin()is the size;rbegin()refers to the last element; the iterator isint* - iterators are raw pointers into the contiguous buffer
reserve relocates by MOVE, not copy or memcpyTrace the output: (select all that apply)
ct::Vector<std::string> v;
v.reserve(2);
v.push_back("a"); v.push_back("b");
v.reserve(8); // relocate the 2 strings
std::cout << v.size() << " " << (v.capacity() >= 8)
<< " " << v[0] << v[1];
- prints
2 1 ab:reserveallocates a bigger buffer, move-constructs the existing elements in, destroys the old ones, frees the old buffer - elements are relocated with
std::uninitialized_move_n, notmemcpyor a copy - prints
0 1: reserving discards the existing elements reserve(8)copies the strings, leaving extra copies alive
Show answer
Answer: A, B
- prints
2 1 ab:reserveallocates a bigger buffer, move-constructs the existing elements in, destroys the old ones, frees the old buffer - elements are relocated with
std::uninitialized_move_n, notmemcpyor a copy
insert shifts+rotates; emplace_back builds in place, returns voidTrace the output: (select all that apply)
ct::Vector<int> v{1, 2, 3};
v.insert(v.begin() + 1, 9); // -> 1 9 2 3
v.emplace_back(4); // build in place at the end
std::cout << v[1] << " " << v.size();
- prints
9 5:insertshifts later elements right and puts 9 at index 1;emplace_backbuilds the new element in place - single-element inserts append then
std::rotatethe value into place;emplaceperfect-forwards its args - prints
2 5:insertoverwrites index 1 emplace_backreturns a reference you must capture
Show answer
Answer: A, B
- prints
9 5:insertshifts later elements right and puts 9 at index 1;emplace_backbuilds the new element in place - single-element inserts append then
std::rotatethe value into place;emplaceperfect-forwards its args
Vector<int> v(5, 7) is count-and-valueTrace the output: (select all that apply)
ct::Vector<int> v(5, 7); // which constructor? std::cout << v.size() << " " << v[0];
- prints
5 7:(5, 7)resolves to the count-and-value constructor — 5 copies of 7 - the range constructor takes a concrete
const_iterator(a pointer), so twoints cannot be mistaken for a range — no concepts orenable_ifneeded - prints
2 5:(5, 7)is treated as a range[5, 7) - this call is ambiguous and fails to compile
Show answer
Answer: A, B
- prints
5 7:(5, 7)resolves to the count-and-value constructor — 5 copies of 7 - the range constructor takes a concrete
const_iterator(a pointer), so twoints cannot be mistaken for a range — no concepts orenable_ifneeded
erase: shift down, size−1, returns an iteratorTrace the output: (select all that apply)
ct::Vector<int> v{10, 20, 30, 40};
auto it = v.erase(v.begin() + 1); // remove 20
std::cout << v.size() << " " << v[1] << " " << *it;
- prints
3 30 30:erasemoves later elements down one, shrinks size to 3, and returns an iterator to the element now at that spot (30) - iterators at or after the erased position are invalidated
- prints
4 30 30:eraseleaves the size unchanged - prints
3 20 20:erasereturns an iterator to the removed value
Show answer
Answer: A, B
- prints
3 30 30:erasemoves later elements down one, shrinks size to 3, and returns an iterator to the element now at that spot (30) - iterators at or after the erased position are invalidated
shrink_to_fit lowers capacity to size; swap exchanges in O(1)Trace the output: (select all that apply)
ct::Vector<int> a{1, 2, 3};
a.reserve(100); // capacity 100, size 3
a.shrink_to_fit(); // capacity -> size (3)
ct::Vector<int> b{7, 8};
a.swap(b); // O(1) exchange
std::cout << a.capacity() << " " << a.size() << " " << a[0];
- prints
2 2 7:shrink_to_fitseta's capacity to 3, thenswapexchangedawithb({7,8}), soa[0]is 7 shrink_to_fitlowers capacity toward size;swapjust exchanges the pointer/size/capacity- prints
3 3 1:swapdoes nothing toa shrink_to_fitraiseda's capacity to matchb
Show answer
Answer: A, B
- prints
2 2 7:shrink_to_fitseta's capacity to 3, thenswapexchangedawithb({7,8}), soa[0]is 7 shrink_to_fitlowers capacity toward size;swapjust exchanges the pointer/size/capacity
front() returns T&; back(), data()Trace the output: (select all that apply)
ct::Vector<int> v{10, 20, 30};
v.front() = 99; // front() returns int&
const ct::Vector<int>& r = v;
std::cout << v[0] << " " << v.back() << " " << (r.data() == v.data());
- prints
99 30 1:front()returns a non-constint&, so assigning through it changesv[0];back()is the last element - on a
constvector,operator[]/front/backreturnconst T&(read-only) - prints
10 30 1:front()returns a copy, so the assignment is a no-op - prints
99 10 1:back()returns the first element
Show answer
Answer: A, B
- prints
99 30 1:front()returns a non-constint&, so assigning through it changesv[0];back()is the last element - on a
constvector,operator[]/front/backreturnconst T&(read-only)
using set)Trace the four printed bits: (select all that apply)
using V = ct::Vector<double>;
std::cout << std::is_same_v<V::value_type, double>
<< std::is_same_v<V::iterator, double*>
<< std::is_same_v<V::const_reference, const double&>
<< std::is_same_v<V::size_type, std::size_t>;
- prints
1111:value_type=double,iterator=double*,const_reference=const double&,size_type=std::size_t - these
usingaliases are what let generic code name the vector's associated types - prints
1011:iteratoris a special class type, notdouble* value_typeis alwaysintregardless ofT
Show answer
Answer: A, B
- prints
1111:value_type=double,iterator=double*,const_reference=const double&,size_type=std::size_t - these
usingaliases are what let generic code name the vector's associated types
Vector<unique_ptr<T>> is movable, not copyableTrace the output: (select all that apply)
ct::Vector<std::unique_ptr<int>> v; v.push_back(std::make_unique<int>(42)); ct::Vector<std::unique_ptr<int>> w = std::move(v); // move the vector std::cout << w.size() << " " << *w[0] << " " << v.size();
- prints
1 42 0: a vector of move-only elements can itself be moved;wtakes the buffer,vis left empty - you can store move-only types like
unique_ptrand still move the whole vector ct::Vector<std::unique_ptr<int>> w = v;(a copy) would compile and duplicate the pointers- prints
1 42 1: moving the vector copies the elements
Show answer
Answer: A, B
- prints
1 42 0: a vector of move-only elements can itself be moved;wtakes the buffer,vis left empty - you can store move-only types like
unique_ptrand still move the whole vector