Exam 2 Practice Problems
A practice set for Exam 2 (Summer 2026). Scope: Lectures 7, 8, 9, 10, and the
HW#2 material. Lectures 7 through 10 (Stroustrup chapters 15 through 18) build one
growable container, the Vector, step by step, so most of the exam is about that
container and the ideas behind it. Sections 5 and 6 are the Vector directly, and
Sections 1 and 3 (the free store and the essential operations) are closely
related to it; together these are the bulk of the exam. Sections 2 and 4 are the
broader array, C-string, template, and exception foundations.
These problems teach concepts; the exam asks the same concepts with different
code, values, and types. Each problem is tagged with the concept it drills (for
example Concept C07). The exam questions will not be these exact problems: they
will trace different code, use different values or element types, or ask from a
different angle (true/false, select-all, or "find the bug"). Memorizing an answer
here will not help; understanding the concept will.
Answer keys. Sections 1 through 5 are answered inline (after Section 5). These
cover the foundations and the observable behavior of std::vector. Section 6 is
the Vector internals (directly from HW#2): those questions appear here, but their
answers are held in a separate file
(CT301_Exam2_Practice_Problems_v1_HW2_Answers.md) released after the HW#2 due
date, so the practice stays useful while you are still building your own container.
Section 7 adds more of the Vector interface: its release items are answered inline,
and its one internals item (Problem 61) is gated like Section 6. No arithmetic is
required anywhere; every question is about what code does, which lines compile, or
which statements are true.
Section 1. Lecture 7: Free store, pointers, destructors
Problem 1 [easy] - Concept C01 (destructors and scope)
struct Noisy {
Noisy() { std::cout << "ctor "; }
~Noisy() { std::cout << "dtor "; }
};
int main() {
Noisy a;
{
Noisy b;
}
std::cout << "end ";
}
What does the program print?
A. ctor ctor end dtor dtor
B. ctor ctor dtor end dtor
C. ctor dtor ctor end dtor
D. ctor ctor end dtor
Problem 2 [medium] - Concept C02 (new/delete pairing)
Which statements are true? (Select all that apply.)
A. new throws std::bad_alloc if it cannot allocate; you do not test its result against nullptr.
B. Memory from new is released with delete, and memory from new[] with delete[]; mixing them is undefined behavior.
C. delete on a null pointer is harmless (it does nothing).
D. new returns nullptr on failure, so you must check for null after every new.
Problem 3 [medium] - Concept C03 (use-after-free)
int* p = new int(10);
delete p;
std::cout << *p; // line X
What best describes line X?
A. It safely prints 10.
B. Undefined behavior: p is now a dangling pointer (use-after-free); the object it named was freed.
C. Compile error.
D. It throws an exception.
Problem 4 [medium] - Concept C04 (double free)
int* a = new int(7);
int* b = a; // b holds the same address
delete a;
delete b; // line X
What best describes line X?
A. It frees a second, independent object.
B. Undefined behavior: the same memory is freed twice (a double free), because a and b alias one allocation.
C. It is safe, because b is a different variable.
D. Compile error.
Section 2. Lecture 8: Arrays, pointers, references, C-style strings
Problem 5 [medium] - Concept C05 (references vs pointers)
int x = 1, y = 2;
int& r = x;
r = y; // line A
int* p = &x;
p = &y; // line B
std::cout << x << " " << y << "\n";
What does the program print?
A. 1 2
B. 2 2
C. 2 1
D. The code does not compile.
Problem 6 [medium] - Concept C06 (array-to-pointer decay)
void f(int* p);
int main() {
int a[3] = {1, 2, 3};
f(a); // line 1
int* p = a; // line 2
int b[3] = a; // line 3
}
Which lines compile?
A. Lines 1 and 2 only
B. Lines 1, 2, and 3
C. Line 2 only
D. Line 1 only
Problem 7 [medium] - Concept C07 (pointer arithmetic)
int a[4] = {10, 20, 30, 40};
int* p = a;
++p;
*p = 99;
What are the array's contents now?
A. {10, 20, 30, 40}
B. {99, 20, 30, 40}
C. {10, 99, 30, 40}
D. {10, 20, 99, 40}
Problem 8 [medium] - Concept C08 (C-style strings)
Which declaration creates a C-style string whose characters you can safely modify in place (for example, s[0] = 'H';)?
A. const char* s = "hello";
B. char* s = "hello";
C. char s[] = "hello";
D. const char s[] = "hello";
Problem 9 [medium] - Concept C09 (alternatives to raw pointers)
Which statements are true? (Select all that apply.)
A. A std::span is like a pointer that also knows how many elements it refers to, so it can range-check.
B. A std::array<int, 8> knows its size at compile time and does not implicitly decay to a pointer.
C. A raw int* parameter tells the function how many elements it points to.
D. not_null is a pointer type that is checked never to hold nullptr.
Problem 10 [easy] - Concept C10 (pass by const reference)
A function takes its parameter by const std::string& rather than by value. Which statements are true? (Select all that apply.)
A. It avoids copying the caller's string into the parameter.
B. The function cannot modify the caller's string through the reference.
C. It makes a full copy of the string, exactly like pass-by-value.
D. To let the function modify the caller's object, you would instead use a non-const std::string&.
Section 3. Lecture 9: Essential operations (copy and move)
Problem 11 [medium] - Concept C11 (copy construction vs copy assignment)
std::string c = a; // line 1
b = a; // line 2 (b already exists)
Which essential operation runs on each line?
A. Line 1: copy constructor. Line 2: copy assignment operator.
B. Line 1: copy assignment operator. Line 2: copy constructor.
C. Both lines: copy constructor.
D. Both lines: copy assignment operator.
Problem 12 [hard] - Concept C12 (const and non-const operator[])
struct Arr {
int d[3] = {1, 2, 3};
int& operator[](int i) { return d[i]; }
int operator[](int i) const { return d[i]; }
};
int main() {
Arr a;
a[0] = 10; // line 1
const Arr b;
int x = b[0]; // line 2
b[0] = 5; // line 3
std::cout << a[0] << " " << x << "\n";
}
Which best describes the result?
A. All three lines compile; the program prints 10 1.
B. Lines 1 and 2 compile, but line 3 does not: on a const Arr, the const overload runs and returns an int by value (an rvalue), which cannot be assigned to. With line 3 removed, the program prints 10 1.
C. None of the lines compile.
D. Lines 1 and 3 compile, but line 2 does not.
Problem 13 [medium] - Concept C13 (rule of zero vs rule of all)
Which class should define NONE of the essential operations (the rule of zero)?
A. A class whose only members are a std::string and a std::vector<int>.
B. A class that owns a raw T* buffer it allocates with new[].
C. A class with a T* data_ that it delete[]s in its destructor.
D. A class that manages a file handle by hand.
Problem 14 [medium] - Concept C14 (shallow vs deep copy)
A class owns a T* buffer it allocates with new[] and frees in its destructor, but it relies on the compiler-generated copy constructor. A copy b = a is made, then b grows (reallocating its buffer). What best describes what goes wrong?
A. Nothing; each object has its own buffer.
B. The compiler-generated copy is shallow: it copies the buffer pointer, so a and b share one buffer. When b reallocates it frees the shared buffer, leaving a's pointer dangling; and at destruction the original buffer is freed twice. The fix is a deep-copying copy constructor (the rule of all).
C. A compile error, because the class has a destructor.
D. A leak, because the copy frees nothing.
Problem 15 [hard] - Concept C15 (move leaves the source empty)
std::vector<int> a = {1, 2, 3};
std::vector<int> b = std::move(a);
Which statements are true? (Select all that apply.)
A. b now holds the three elements; the move transferred the internal buffer instead of copying it.
B. a is left in a valid but unspecified state (for a vector, empty): you may assign to it or destroy it, but should not rely on its contents.
C. Calling a.size() afterward is undefined behavior.
D. The elements were deep-copied, so both a and b hold them.
Problem 16 [very hard] - Concept C16 (return by value moves or elides)
std::vector<std::string> make() {
std::vector<std::string> v;
v.push_back("a");
v.push_back("b");
return v; // line R
}
int main() {
std::vector<std::string> s = make();
std::cout << s.size() << "\n";
}
Which statements are true? (Select all that apply.)
A. Returning the local at line R enables a move (or copy elision), so the buffer is not deep-copied on the way out.
B. If moved, the move transfers the buffer and leaves the temporary empty and safe to destroy.
C. s ends up with two elements.
D. The local's buffer leaks because it is never freed.
Problem 17 [medium] - Concept C17 (std::move is a cast)
std::string a = "hi";
std::string&& r = std::move(a); // bind an rvalue reference to a
std::cout << a; // line X
What does line X print, and why?
A. Nothing: std::move emptied a.
B. hi: std::move(a) is only a cast to an rvalue. By itself it moves nothing; a becomes moved-from only when something actually constructs or assigns from that rvalue.
C. Undefined behavior.
D. Compile error.
Section 4. Lecture 10: Templates and exceptions
Problem 18 [easy] - Concept C18 (function template deduction)
template <typename T>
T max_of(T a, T b) { return a > b ? a : b; }
Which statements are true? (Select all that apply.)
A. max_of(3, 4) compiles; T is deduced as int.
B. max_of(3, 4.0) compiles; T is deduced as double.
C. max_of(3, 4.0) fails to deduce T (one argument is int, the other double); you must write max_of<double>(3, 4.0) or convert an argument.
D. A function template definition must be placed in a .cpp file, not a header.
Problem 19 [medium] - Concept C19 (class template argument deduction)
template <typename T> struct Box { T v; Box(T x) : v{x} {} };
Box<int> bi {5}; // line 1
Box bd {3.14}; // line 2
Box be; // line 3
Which lines compile?
A. Lines 1 and 2 only: line 1 is explicit, line 2 uses class template argument deduction (CTAD) to deduce Box<double>, and line 3 has no argument to deduce T from.
B. All three lines.
C. Line 1 only.
D. None of the lines.
Problem 20 [medium] - Concept C20 (template instantiation)
Vector<T> is a class template. Which is true?
A. You must write a separate class by hand for each element type.
B. The compiler generates a distinct class from the one template for each element type you use (Vector<int>, Vector<std::string>, ...); this is template instantiation.
C. Vector<T> works only for built-in types.
D. The element type must be int.
Problem 21 [medium] - Concept C21 (concepts constrain templates)
template <std::integral T>
T twice(T x) { return x + x; }
twice(3); // line 1
twice(2.5); // line 2
Which is true?
A. Both lines compile.
B. Line 1 compiles (int satisfies std::integral); line 2 fails because double is not integral. A concept constrains the type parameter and rejects arguments that do not satisfy it.
C. Neither line compiles.
D. Line 2 compiles, line 1 fails.
Problem 22 [medium] - Concept C22 (exceptions and stack unwinding)
int safe_div(int a, int b) {
if (b == 0) throw std::out_of_range("zero");
return a / b;
}
int main() {
try { std::cout << safe_div(10, 0); }
catch (const std::out_of_range&) { std::cout << "caught "; }
std::cout << "done";
}
What does the program print?
A. caught done
B. Nothing; the program aborts.
C. done
D. 0done
Problem 23 [medium] - Concept C23 (at throws)
std::vector<int> v = {1, 2, 3};
int x = v.at(5); // line X
What happens at line X?
A. It returns 0.
B. Undefined behavior.
C. It throws std::out_of_range (index 5 is past the end).
D. It returns the last element.
Problem 24 [medium] - Concept C24 (smart pointers)
std::unique_ptr<Shape> read_shape(std::istream& is); // returns a Circle, Triangle, ...
Which statements are true? (Select all that apply.)
A. The returned std::unique_ptr<Shape> deletes the object automatically when it goes out of scope; the caller writes no delete.
B. std::unique_ptr is move-only: you can move it out of the function but cannot copy it.
C. If you needed several co-owners of the same shape, you would use std::shared_ptr<Shape>, which is copyable and frees the object when the last owner is destroyed.
D. Returning a raw Shape* would be safer than the unique_ptr.
Section 5. The Vector: behavior (related to HW#2)
These are about how a growable container behaves from the outside: ownership of
its buffer, element access, copy and move, capacity and growth, and the standard
operations. They are answered inline below (after this section), because knowing
how std::vector behaves does not tell you how to implement it.
Problem 25 [easy] - Concept C25 (ownership of the buffer)
In a hand-built Vector<T> (and in std::vector<T>), where do the stored elements live, and who frees them?
A. On the call stack; they are freed automatically at the end of the enclosing scope.
B. On the free store, in a buffer the Vector allocates; the Vector owns it and frees it in its destructor.
C. In static storage shared by all vectors.
D. Nowhere until you call reserve.
Problem 26 [medium] - Concept C26 (a buffer owner needs a destructor)
A Vector<T> allocates its element buffer in its constructor but frees it nowhere. What happens each time such a Vector is destroyed?
A. Nothing; the buffer is freed automatically.
B. The buffer leaks: raw heap memory is reclaimed only if the destructor frees it.
C. A double free.
D. A compile error.
Problem 27 [medium] - Concept C27 (operator[] vs at)
A Vector<T> offers both operator[](i) and at(i). What is the difference?
A. They are identical.
B. at(i) range-checks and throws std::out_of_range on a bad index; operator[](i) is unchecked and is undefined behavior out of range.
C. operator[](i) throws; at(i) is unchecked.
D. at(i) only works on const vectors.
Problem 28 [medium] - Concept C28 (copy is deep)
std::vector<int> a = {1, 2, 3};
std::vector<int> b = a; // copy
b[0] = 99;
std::cout << a[0] << " " << b[0] << "\n";
What does the program print?
A. 99 99 (the two vectors share storage)
B. 1 99 (copying a vector is a deep copy; the elements are independent)
C. 1 1
D. Compile error
Problem 29 [medium] - Concept C29 (size vs capacity)
std::vector<int> v;
v.reserve(10);
v.push_back(1);
v.push_back(2);
Which statement is true?
A. v.size() is 2 and v.capacity() is at least 10; neither push_back reallocated.
B. v.size() is 10 after reserve(10).
C. v.capacity() is 2.
D. reserve(10) constructs 10 ints, so v.size() is 10.
Problem 30 [medium] - Concept C30 (reserve vs resize)
On an empty std::vector<int> v, what is the difference between v.reserve(10) and v.resize(10)?
A. They are the same.
B. reserve(10) raises capacity to at least 10 but leaves size 0 (no elements created); resize(10) makes size 10, value-initializing 10 elements.
C. reserve(10) creates 10 elements; resize(10) only changes capacity.
D. Both create 10 elements.
Problem 31 [hard] - Concept C31 (reallocation invalidates handles)
std::vector<int> v = {1, 2, 3};
int* p = &v[0];
v.push_back(4); // may reallocate
std::cout << *p << "\n"; // line X
Which is the BEST description of line X?
A. It always safely prints 1.
B. If the push_back reallocated the buffer, p now dangles and *p is undefined behavior: pointers and iterators into a vector are invalidated when it reallocates.
C. It prints 4.
D. Compile error.
Problem 32 [hard] - Concept C32 (geometric growth)
std::vector<int> v; // empty
for (int i = 0; i < 100; ++i)
v.push_back(i);
Which statements are true about this loop? (Select all that apply.)
A. The vector grows its capacity geometrically (for example, doubling), so the 100 push_backs trigger only a handful of reallocations, not 100.
B. Each reallocation moves or copies the existing elements into a new, larger buffer and frees the old one.
C. Any pointer or iterator taken before a reallocation is invalidated by that reallocation.
D. The vector reallocates on every single push_back, which would make the loop quadratic.
Problem 33 [hard] - Concept C33 (move-only elements)
std::vector<std::unique_ptr<int>> v;
v.push_back(std::make_unique<int>(1));
v.push_back(std::make_unique<int>(2));
std::vector<std::unique_ptr<int>> w = v; // line C
std::vector<std::unique_ptr<int>> x = std::move(v); // line M
Which lines compile?
A. Both line C and line M.
B. Line M only. Line C does not compile: std::unique_ptr is move-only, so a vector of unique_ptr cannot be copied, only moved.
C. Line C only.
D. Neither.
Problem 34 [medium] - Concept C34 (shrink_to_fit)
std::vector<int> v;
v.reserve(50);
v.push_back(1); v.push_back(2); v.push_back(3);
v.shrink_to_fit();
Which statement is true?
A. v.size() is now 50.
B. shrink_to_fit reduces capacity toward size (here to 3) and leaves the three element values unchanged; size stays 3.
C. It deletes the elements.
D. Capacity is unchanged at 50 or more.
Problem 35 [medium] - Concept C35 (swap)
std::vector<int> a = {1};
std::vector<int> b = {7, 8};
a.swap(b);
What is true after the swap?
A. a is {1} and b is {7, 8} (unchanged).
B. a is {7, 8} and b is {1}: swap exchanges the two vectors' contents (and their sizes and capacities).
C. Both become {1, 7, 8}.
D. Compile error.
Problem 36 [easy] - Concept C36 (equality)
When are two std::vector<int> values equal under ==?
A. Whenever they have the same capacity.
B. When they have the same size and each corresponding element is equal.
C. When they refer to the same buffer in memory.
D. Never; vectors are not comparable.
Problem 37 [medium] - Concept C37 (clear and pop_back keep capacity)
A std::vector<int> is filled with 5 elements, so its capacity is some value C. Which statement is true?
A. clear() sets size to 0 and frees the buffer, so capacity becomes 0.
B. clear() sets size to 0 but leaves capacity at C (the buffer is kept for reuse); pop_back() reduces size by one and also leaves capacity unchanged.
C. pop_back() sets capacity equal to the new size.
D. Both clear() and pop_back() reset capacity to 0.
Problem 38 [hard] - Concept C38 (erase shifts elements)
std::vector<int> v = {0, 10, 20, 30};
v.erase(v.begin() + 1); // remove the element 10
What is v now, and what about iterators?
A. {0, 20, 30}: erasing shifts the later elements down by one, size becomes 3, and any iterator at or after the erased position is invalidated.
B. {0, 0, 20, 30}.
C. {10, 20, 30}: it removes the first element.
D. {0, 10, 20, 30}: nothing changes.
Problem 39 [easy] - Concept C39 (iterators and range-for)
std::vector<int> v = {1, 2, 3};
int s = 0;
for (int x : v) s += x;
Which statement is true?
A. The range-for visits every element via begin()..end(), so s ends at 6; end() refers to one past the last element, which you never dereference.
B. It visits only the first element.
C. end() points at the last element.
D. Compile error.
Problem 40 [hard] - Concept C40 (RAII keeps it leak-safe)
A function builds a std::vector<int> and then calls something that throws. Why does the vector's memory NOT leak?
A. It does leak; you must catch the exception and free it by hand.
B. The vector is an RAII object: as the exception unwinds the stack, the vector's destructor runs and frees its buffer automatically. Managing the buffer with raw new[]/delete[] could leak if the throw happens between them.
C. Exceptions cannot occur after a vector is created.
D. The compiler frees all memory when an exception is thrown.
Problem 41 [medium] - Concept C41 (driver value semantics)
// sorted_copy(v) returns a new sorted vector and does not change v.
std::vector<int> v = {3, 1, 2};
std::vector<int> s = sorted_copy(v);
Which statement is true?
A. v is now {1, 2, 3} (it sorted v in place).
B. s is {1, 2, 3} and v is still {3, 1, 2}: sorted_copy returns a sorted COPY and leaves its input unchanged (value semantics).
C. s and v share storage.
D. It returns nothing (void).
Answers: Sections 1 through 5
Problem 1: B (ctor ctor dtor end dtor). a and b are constructed in turn. b is a local of the inner block, so its destructor runs at that block's closing brace. Then end prints, and finally a is destroyed when main returns. Locals are destroyed in reverse order of construction, at the end of their scope. (learncpp 15.4 Introduction to destructors.)
Problem 2: A, B, C. new reports failure by throwing std::bad_alloc, not by returning null, so you do not null-check it (A). new/delete and new[]/delete[] must be paired (B). delete nullptr is a defined no-op (C). D is false: because new throws, it never returns null. (learncpp 19.1 Dynamic memory allocation.)
Problem 3: B. After delete p, the int it pointed at no longer exists; p is a dangling pointer. Reading *p is a use-after-free, which is undefined behavior. (learncpp 19.1 Dynamic memory allocation; 12.7 pointers.)
Problem 4: B. a and b hold the same address, so delete a frees the allocation and delete b frees it again: a double free, which is undefined behavior. A buffer must be freed exactly once. (learncpp 19.1 Dynamic memory allocation.)
Problem 5: B (2 2). A reference cannot be reseated: int& r = x; makes r another name for x, so r = y; writes y's value (2) into x. A pointer can be reseated: p = &y; only changes what p points at and modifies no int. So x == 2 and y == 2. (learncpp 12.3 Lvalue references; 12.7 Introduction to pointers.)
Problem 6: A. Passing an array (line 1) and initializing a pointer from it (line 2) both decay the array to a pointer to its first element. Line 3 fails: you cannot copy-initialize one array from another. (learncpp 17.8 C-style array decay.)
Problem 7: C ({10, 99, 30, 40}). p starts at a[0]; ++p advances it by one element to a[1]; *p = 99 writes through it. Indexing is pointer arithmetic plus a dereference: a[i] is *(a + i). (learncpp 17.9 Pointer arithmetic and subscripting.)
Problem 8: C. char s[] = "hello"; copies the literal into a writable local array, so s[0] = 'H'; is fine. A and D are const (writing is a compile error). B makes s point at the read-only string literal; writing through it is undefined behavior (and binding a literal to a non-const char* is a deprecated conversion compilers accept only with a warning). (learncpp 17.10 C-style strings.)
Problem 9: A, B, D. A span is a pointer plus a length (the course's span is range-checked); a std::array carries its size in its type and does not decay to a pointer; not_null is checked never to be null. C is false: a raw pointer carries only an address, not a count, which is why these safer types exist. (PPP Ch. 16; learncpp 17.x arrays and pointers.)
Problem 10: A, B, D. A const T& parameter binds to the caller's object without copying it (A) and forbids modifying it (B); to allow modification you use a non-const T& (D). C is false: a reference is not a copy. (learncpp 12.5 Pass by const lvalue reference.)
Problem 11: A. Line 1 (std::string c = a;) initializes a new object, which is copy construction. Line 2 (b = a;) assigns to an object that already exists, which is copy assignment. The = in a declaration is initialization, not assignment. (learncpp 14.14 Copy constructor; 21.12 Copy assignment.)
Problem 12: B. a[0] = 10 calls the non-const operator[], which returns int& (assignable). int x = b[0] calls the const overload on the const Arr, returning an int by value (fine to read). b[0] = 5 also selects the const overload (because b is const) and you cannot assign to the returned rvalue, so line 3 is a compile error. With line 3 removed, the program prints 10 1. Providing both a non-const (T&) and a const overload is the standard container pattern. (learncpp 21.9 Overloading the subscript operator; 14.12 Const objects and const member functions.)
Problem 13: A. Only class A owns nothing raw: a std::string and a std::vector<int> manage their own memory, so the compiler-generated essential operations are correct and you should define none (the rule of zero). B, C, and D each manage a raw resource by hand and fall under the rule of all. Prefer members that manage themselves. (learncpp 21.13 Shallow vs. deep copying.)
Problem 14: B. This is the shallow-copy trap. The compiler-generated copy copies the buffer pointer, so a and b share one buffer. When b grows it frees the old shared buffer, leaving a's pointer dangling; and when both objects are destroyed, the original buffer is freed twice. The fix is a deep-copying copy constructor and matching copy assignment, that is, the rule of all. (learncpp 21.13 Shallow vs. deep copying; 22.3 Move constructors and assignment.)
Problem 15: A, B. std::move(a) lets b's move constructor take over a's internal buffer instead of copying the elements (A true, D false). A moved-from std::vector is in a valid but unspecified state, which for a vector means empty; calling a.size() is well-defined (in practice it returns 0), so C is false. (learncpp 22.3 Move constructors and move assignment; 22.4 std::move.)
Problem 16: A, B, C. Returning a local container (line R) is a prvalue context: the compiler elides the copy or, failing that, moves, so the buffer is not deep-copied on return (A). The move transfers the buffer and leaves the temporary empty and safe to destroy (B). The two pushed elements end up in s (C). Nothing leaks: every buffer is owned by some vector whose destructor frees it (D false). (learncpp 22.3 Move constructors and move assignment.)
Problem 17: B (hi). std::move(a) is just a cast that produces an rvalue referring to a; binding it to std::string&& r moves nothing. a would become moved-from only if some constructor or assignment actually consumed that rvalue. Since nothing did, a is unchanged and prints hi. (learncpp 22.4 std::move.)
Problem 18: A, C. max_of(3, 4) deduces T = int. max_of(3, 4.0) gives conflicting deductions for the single T, so it fails unless you specify max_of<double>(...) or convert an argument. Templates live in headers, so D is false. (learncpp 11.6 Function templates; 11.7 Function template instantiation.)
Problem 19: A. Line 1 names the type explicitly. Line 2 uses class template argument deduction (CTAD): from Box bd {3.14} the compiler deduces Box<double>. Line 3 gives no constructor argument, so there is nothing to deduce T from, and it does not compile. (learncpp 13.14 Class template argument deduction.)
Problem 20: B. A class template is a pattern. When you use Vector<int> and Vector<std::string>, the compiler instantiates the template once per element type, generating a distinct class for each. It works for built-in and user-defined element types alike, so A, C, and D are false. (learncpp 13.13 Class templates; 11.7 Template instantiation.)
Problem 21: B. std::integral is a concept: a compile-time requirement on T. twice(3) deduces T = int, which satisfies it, so line 1 compiles. twice(2.5) deduces T = double, which is not integral, so the call is rejected at compile time with a clear constraint error. Concepts (for example template <Element T>) state requirements up front. (learncpp 11.7 concepts; cppreference Constraints and concepts.)
Problem 22: A (caught done). safe_div(10, 0) throws std::out_of_range. The throw abandons the std::cout << ... and unwinds to the matching catch, which prints caught. Execution then continues after the try/catch and prints done. As the stack unwinds, destructors of local objects run, which is what makes RAII cleanup reliable. (learncpp 27.2 Basic exception handling.)
Problem 23: C. at(i) is the range-checked access form: index 5 is past the end of a 3-element vector, so at(5) throws std::out_of_range. (operator[](5) would instead be undefined behavior.) (cppreference std::vector::at; learncpp 27.2 Basic exception handling.)
Problem 24: A, B, C. A unique_ptr is an owning, RAII smart pointer: it frees the object automatically (A) and is move-only (B). For shared ownership you use shared_ptr, which is copyable and reference-counted (C). A raw Shape* is less safe (the caller must remember to delete it and can leak or double-free), so D is false. (learncpp 22.5 std::unique_ptr; 22.6 std::shared_ptr.)
Problem 25: B. A growable container keeps its elements in a buffer on the free store, reached through a pointer member. The container object itself may live on the stack, but the element storage is heap-allocated, owned by the container, and freed in its destructor. This is the ownership model of both std::vector and your ct::Vector. (learncpp 19.2 Dynamically allocating arrays.)
Problem 26: B. Raw heap memory is never reclaimed automatically; it is freed only when the owner releases it. If the destructor does not free the buffer, every Vector that is destroyed leaks its buffer. Acquiring in the constructor and releasing in the destructor (RAII) is what prevents this. (learncpp 19.1 Dynamic memory allocation; 15.4 Destructors.)
Problem 27: B. at(i) does a range check and throws std::out_of_range on a bad index, while operator[](i) is unchecked and is undefined behavior out of range (fast, no check). The course Vector and the standard vector both follow this convention. (cppreference std::vector::at / operator[].)
Problem 28: B (1 99). Copy-initializing b from a makes an independent copy: the elements are duplicated into separate storage, so b[0] = 99 changes only b and a[0] is still 1. A shallow copy would print 99 99 and double-free later; the library does the deep copy, which is what a correct Vector copy constructor must do. (learncpp 21.13 Shallow vs. deep copying.)
Problem 29: A. reserve(10) raises capacity to at least 10 without changing size or constructing any elements, so size stays 0 until you add elements. After the two push_backs, size is 2 and capacity is still at least 10, and because the room was pre-reserved, neither push_back reallocated. B and D confuse capacity with size; C is wrong. (cppreference std::vector::reserve and capacity.)
Problem 30: B. reserve(n) is about capacity only: it ensures room for n elements but does not change the size or create elements. resize(n) is about size: it makes the vector have n elements, value-initializing any new ones (and destroying surplus ones if shrinking). So after reserve(10) the vector is still empty; after resize(10) it has 10 elements. (cppreference std::vector::reserve and resize.)
Problem 31: B. &v[0] saves the address of the first element's current storage. A push_back that exceeds capacity allocates a new buffer, moves the elements over, and frees the old one, so the saved pointer p now points into freed memory: dereferencing it is undefined behavior. Reallocation invalidates all pointers, references, and iterators into the vector. (cppreference std::vector::push_back iterator-invalidation note.)
Problem 32: A, B, C. A vector keeps push_back at amortized O(1) by growing capacity geometrically (commonly doubling), so 100 inserts cause only on the order of log-many reallocations, not 100 (A true, D false). Each reallocation allocates a larger buffer, transfers the existing elements, and frees the old buffer (B). Any pointer or iterator taken before a reallocation is invalidated (C). (cppreference std::vector capacity growth and push_back.)
Problem 33: B. Copying a container must copy each element, so a container is copyable only if its element type is copyable. std::unique_ptr deletes its copy operations (it is move-only), so std::vector<std::unique_ptr<int>> cannot be copied (line C is a compile error) but can be moved (line M compiles). Supporting move-only element types is part of what makes a container general. (learncpp 22.5 std::unique_ptr; 22.1 move semantics.)
Problem 34: B. reserve(50) raised capacity to at least 50, but only three elements were added, so size is 3. shrink_to_fit() is a non-binding request to reduce capacity toward size; in practice it relocates the three elements into a buffer of capacity 3. The element values are unchanged and size stays 3. (cppreference std::vector::shrink_to_fit.)
Problem 35: B. swap exchanges the entire contents of the two vectors, so a becomes {7, 8} and b becomes {1} (their sizes and capacities trade as well). It does not merge them or copy element by element. (cppreference std::vector::swap.)
Problem 36: B. Two vectors compare equal when they have the same size and every corresponding pair of elements is equal; capacity and buffer identity are irrelevant. (cppreference std::vector operator==.)
Problem 37: B. clear() destroys the elements and sets size to 0 but keeps the allocated buffer, so capacity is unchanged (the buffer is held for reuse). pop_back() destroys the last element and reduces size by one, also without changing capacity. Neither shrinks the buffer; shrink_to_fit is the operation that does. (cppreference std::vector::clear, pop_back.)
Problem 38: A. erase at a position removes that element and shifts every later element down one to close the gap, so {0, 10, 20, 30} becomes {0, 20, 30} and size becomes 3. Iterators and references at or after the erased position are invalidated. (cppreference std::vector::erase.)
Problem 39: A. A range-for over a vector iterates from begin() to end(), visiting every element, so s becomes 6. end() is a one-past-the-last position used only to stop the loop; dereferencing it is invalid. (cppreference range-for; std::vector::begin/end.)
Problem 40: B. The vector is an RAII (scoped resource management) object: its buffer is tied to its lifetime. When an exception propagates, the stack unwinds and the local vector's destructor runs, freeing the buffer automatically, so there is no leak. A hand-managed raw buffer leaks if the throw happens between allocation and release, which is why RAII is preferred. (learncpp 27.x exceptions and resource management; PPP Ch. 18, RAII.)
Problem 41: B. sorted_copy has value semantics: it makes a copy of its input, sorts the copy, and returns it. The original v is untouched, so v stays {3, 1, 2} while s is {1, 2, 3}. Returning a new value rather than mutating the argument is the contract of the driver. (cppreference std::sort; the HW#2 driver specification.)
Section 6. The Vector: internals (directly from HW#2)
These questions are about how the container is built: the manual-memory mechanism
and the memory-management algorithms you implement in HW#2. The questions are
here so you can practice; the answers are released after the HW#2 due date in
CT301_Exam2_Practice_Problems_v1_HW2_Answers.md, so working them does not hand
you your homework. (On the exam these same concepts appear with different code and
values, as everywhere else.)
Problem 42 [hard] - Concept C42 (allocation is separate from construction)
HW#2 separates allocating raw storage from constructing elements (::operator new for storage, placement new to construct). Which sequence correctly RELEASES a buffer that holds live elements?
A. Call delete[] once on the buffer.
B. Run each live element's destructor (for example with std::destroy_n), then release the raw storage with ::operator delete. Because allocation and construction are separate steps, destruction and deallocation are separate too.
C. Call ::operator delete only; the elements clean themselves up.
D. Call free() on the buffer.
Problem 43 [very hard] - Concept C43 (push_back when full)
When push_back is called on a Vector whose size equals its capacity, what must happen?
A. Write the new element one past the end of the current buffer.
B. Allocate a larger buffer, move (or copy) the existing elements into it, destroy the old elements and free the old buffer, then store the new element.
C. Throw, because the vector is full.
D. The buffer resizes itself in place.
Problem 44 [hard] - Concept C44 (growth policy)
ct::Vector<int> v; // empty, capacity 0
// push_back eight times, then once more (a ninth)
Under the course growth policy, what is the capacity right after the 8th push, and right after the 9th?
A. 8, then 9.
B. 8, then 16: capacity goes 0 to 8 (the first non-empty capacity), then doubles to 16. The policy is reserve(space == 0 ? 8 : 2 * space).
C. 10, then 20.
D. It grows by one each time, so 8, then 9.
Problem 45 [hard] - Concept C45 (reserve implementation)
What does reserve(n) do when n is greater than the current capacity?
A. Nothing until you push_back.
B. Allocate a new buffer with room for n, move-construct the existing elements into it, destroy the old elements and free the old buffer, and set capacity to n. Size is unchanged and no new elements are created.
C. Create n new value-initialized elements.
D. Change size to n.
Problem 46 [hard] - Concept C46 (deep-copy copy constructor)
How does the copy constructor make an independent copy of another Vector?
A. Copy the other vector's buffer pointer.
B. Allocate a new buffer sized to the source's size, copy-construct each source element into that raw storage, and set the new size and capacity, so the two vectors share no storage.
C. Call std::move on the source.
D. Nothing; the compiler-generated copy is correct.
Problem 47 [medium] - Concept C47 (move constructor)
How does the move constructor transfer state from the source Vector?
A. Deep-copy the elements into new storage.
B. Take over the source's buffer pointer, size, and capacity, then set the source's pointer to null and its size and capacity to 0, leaving the moved-from object empty and safe to destroy.
C. Swap one element at a time.
D. Free the source's buffer.
Problem 48 [hard] - Concept C48 (element lifetime)
A Vector<std::string> holds three strings and is then destroyed. What must the destructor do?
A. Release the buffer memory only.
B. Run each string element's destructor first (so each string frees its own heap text), then release the buffer storage. Freeing the storage without destroying the elements leaks the elements' own resources.
C. Nothing extra.
D. Destroy only the first string.
Problem 49 [hard] - Concept C49 (shrink_to_fit implementation)
How is shrink_to_fit implemented when size is less than capacity?
A. Just set the capacity field equal to size.
B. Allocate a new buffer of exactly size, move the elements into it, destroy the old elements and free the old buffer, and set capacity to size. Lowering capacity means reallocating, which is why it can invalidate outstanding pointers and iterators.
C. It cannot be done by hand.
D. Destroy the elements.
Problem 50 [medium] - Concept C50 (swap implementation)
How can a member swap exchange two Vectors in constant time?
A. Copy every element from each into the other.
B. Swap the three member fields (the buffer pointer, the size, and the capacity) between the two objects. No elements are moved or copied, so it is O(1).
C. It must be O(n).
D. Reallocate both buffers.
Problem 51 [hard] - Concept C51 (erase implementation)
How does erase(it) remove an interior element while keeping order?
A. Set the element to 0.
B. Move (assign) each later element down one position to fill the gap, destroy the now-duplicated last element, and decrease size by one; capacity is unchanged.
C. Free the buffer.
D. Swap the element with the last one and stop.
Problem 52 [medium] - Concept C52 (iterators are pointers)
In a contiguous Vector<T>, what are begin() and end()?
A. Special iterator objects unrelated to the buffer.
B. begin() is the address of the first element (elements_) and end() is one past the last (elements_ + size_); a Vector iterator is just a T*, so end() - begin() == size().
C. begin() is element 0 and end() is the last element.
D. They return copies of the elements.
Problem 53 [hard] - Concept C53 (the const_iterator difference)
In HW#2 the range members (the range constructor, assign(first, last), and range insert) take a concrete const_iterator (a pointer), not a template parameter. Why?
A. To save typing.
B. A pointer cannot be mistaken for an integer, so Vector<int> v(5, 7) unambiguously resolves to the count-and-value constructor (five copies of 7), with no concepts or enable_if needed. This is one of the two documented differences from std::vector.
C. Pointers are faster than templates.
D. The standard requires it.
Problem 54 [medium] - Concept C54 (driver implementation)
largest(v) must return the largest element of a non-empty Vector<int>. Which approach is correct using only the public interface?
A. Read the private buffer pointer directly.
B. Start with the first element as the running maximum, then iterate over the rest with begin()..end() (or by index), updating the maximum, and return it. (contains, count_even, and sorted_copy likewise use only the public interface: iterate and test, or copy then sort.)
C. Sort the vector in place and return its last element.
D. Always return v[0].
Answers: Section 6
Released after the HW#2 due date. See
CT301_Exam2_Practice_Problems_v1_HW2_Answers.md.
Section 7. More of the Vector interface
These extend Sections 4 and 5 with parts of the interface the Exam 2 Topics list
calls out: reverse iterators, front/back/data, insert/emplace_back,
assign, the member type aliases, and the exception reserve/resize throw.
They are observable behavior, so their answers are inline (after this section).
One item (Problem 61) is the internal mechanism behind insert/emplace and is
gated like Section 6: its answer is in the same after-the-due-date file.
Problem 55 [medium] - Concept C55 (reverse iterators)
ct::Vector<int> v = {1, 2, 3, 4};
std::string s;
for (auto it = v.rbegin(); it != v.rend(); ++it)
s += std::to_string(*it);
What is s?
A. "1234"
B. "4321"
C. "" (empty)
D. Compile error
Problem 56 [easy] - Concept C56 (front, back, data)
ct::Vector<int> v = {10, 20, 30};
Which statements are true? (Select all that apply.)
A. v.front() is 10 and v.back() is 30.
B. v.data() returns a pointer to the first element's storage, so v.data()[1] is 20.
C. front() returns a reference, so v.front() = 99; changes the first element.
D. back() removes the last element and returns it.
Problem 57 [medium] - Concept C57 (insert and emplace_back)
ct::Vector<int> v = {1, 2, 3};
auto it = v.insert(v.begin() + 1, 9);
What is v now, and what does insert return?
A. v is {1, 9, 2, 3}; it points at the inserted 9.
B. v is {9, 1, 2, 3}; it points at the front.
C. v is {1, 2, 3, 9}; it points at the end.
D. v is unchanged.
Problem 58 [medium] - Concept C58 (assign replaces contents)
ct::Vector<int> v = {1, 2, 3, 4, 5};
v.assign(2, 7);
What is v now?
A. {1, 2, 3, 4, 5, 7, 7}
B. {7, 7}
C. {2, 7}
D. {7, 7, 3, 4, 5}
Problem 59 [medium] - Concept C59 (member type aliases)
A ct::Vector<T> defines member type aliases with using. For ct::Vector<int>, which are correct? (Select all that apply.)
A. value_type is int.
B. iterator is int* (a contiguous container's iterator is a raw pointer).
C. size_type is a signed type such as int.
D. reference is int&.
Problem 60 [medium] - Concept C60 (length_error from reserve/resize)
ct::Vector<int> v;
v.reserve(v.max_size() + 1); // line X
What happens at line X?
A. It returns normally with a very large capacity.
B. It throws std::length_error, because the request exceeds max_size().
C. It throws std::out_of_range.
D. Undefined behavior.
Answers: Section 7 (release items)
Problem 55: B ("4321"). rbegin() starts at the last element and rend() is one before the first, so iterating rbegin()..rend() visits the elements last-to-first: 4 3 2 1. crbegin()/crend() do the same on a const vector. (cppreference std::vector::rbegin/rend; the Vector's reverse_iterator is std::reverse_iterator<iterator>.)
Problem 56: A, B, C. front() and back() return references to the first and last elements (A), so they are assignable (C); data() is a pointer to the contiguous buffer, so data()[1] is the second element (B). D is false: back() only accesses the last element; pop_back() is what removes it. (cppreference std::vector::front/back/data.)
Problem 57: A. insert(pos, x) places x at pos, shifting the later elements one position to the right, and returns an iterator to the newly inserted element, so v becomes {1, 9, 2, 3} and it points at the 9. (emplace_back constructs an element in place at the end; in HW#2 it returns void, unlike the standard library's reference return.) (cppreference std::vector::insert.)
Problem 58: B ({7, 7}). assign replaces the entire contents: assign(2, 7) makes v hold exactly two 7s, so its size becomes 2 (the old five elements are gone). assign(first, last) and assign(init-list) likewise replace rather than append. (cppreference std::vector::assign.)
Problem 59: A, B, D. value_type is the element type T (here int), iterator is T* because a contiguous container's iterator is just a pointer, and reference is T&. C is false: size_type is an unsigned type (std::size_t). The Vector defines the whole family (value_type, size_type, difference_type, pointer, reference, iterator, const_iterator, reverse_iterator, ...). (cppreference container member types.)
Problem 60: B. reserve and resize throw std::length_error when the requested element count exceeds max_size() (the largest number of elements the vector could ever hold). Contrast at(), which throws std::out_of_range for a bad index. (cppreference std::vector::reserve/resize/max_size.)
Section 7 (gated): the insert/emplace mechanism
Problem 61 [hard] - Concept C61 (insert/emplace mechanism)
In HW#2, a single-element insert(pos, value) keeps the elements in order without a hand-written shift loop. How does it work?
A. It writes value directly at pos, overwriting what was there.
B. It appends the element at the end (via push_back, which handles any growth), then uses std::rotate to move that element back to pos.
C. It allocates a brand-new buffer on every insert.
D. It shifts each later element down by hand in a loop, then writes value.
Answer: Problem 61
Gated. Released after the HW#2 due date in
CT301_Exam2_Practice_Problems_v1_HW2_Answers.md.
Coverage
| Section | Focus | Problems | Answers |
|---|---|---|---|
| 1 | L7: free store, pointers, destructors | 1-4 | inline |
| 2 | L8: arrays, pointers, references, C-strings | 5-10 | inline |
| 3 | L9: essential operations (copy and move) | 11-17 | inline |
| 4 | L10: templates and exceptions | 18-24 | inline |
| 5 | The Vector: behavior (related to HW#2) | 25-41 | inline |
| 6 | The Vector: internals (directly from HW#2) | 42-54 | gated |
| 7 | More of the Vector interface (release) + 1 gated internals item | 55-61 | inline / gated (P61) |
61 problems. The Vector and its closely related material (Sections 1, 3, 5, 6, and the Vector parts of 7) are the bulk of the set, as on the exam, where 70 to 80 percent is HW#2 or related material. Each problem is tagged with the concept it tests; the exam asks those same concepts with different code, values, and types.