Code-Tracing Questions¶
Twelve programs to trace by hand, without running them. For each, write down the exact output (or say "does not compile" / "undefined behaviour"), then check the answers.
Tracing is the fastest way to find a gap in your understanding, because it forces you to commit to a prediction. Write your answer before you check.
How to trace well: write down the order of construction and destruction explicitly; track each variable's value line by line; and for anything polymorphic, say which function is called and why.
T1. (classes, state)
#include <iostream>
class Counter {
public:
Counter() : value_(0) {}
void increment() { ++value_; }
int value() const { return value_; }
private:
int value_;
};
int main() {
Counter a, b;
a.increment(); a.increment(); b.increment();
std::cout << a.value() << ' ' << b.value() << '\n';
}
T2. (construction order)
#include <iostream>
class Engine { public: Engine() { std::cout << "E+ "; } ~Engine() { std::cout << "E- "; } };
class Car {
public:
Car() : engine_(), plate_("X") { std::cout << "C+ "; }
~Car() { std::cout << "C- "; }
private:
Engine engine_;
std::string plate_;
};
int main() { Car c; }
T3. (member init order)
#include <iostream>
class Box {
public:
Box() : b_(a_ + 1), a_(5) { std::cout << a_ << ' ' << b_ << '\n'; }
private:
int a_, b_;
};
int main() { Box x; }
T4. (static)
#include <iostream>
class Id {
public:
Id() : n_(++next_) {}
int n() const { return n_; }
static int count() { return next_; }
private:
int n_;
static int next_;
};
int Id::next_ = 0;
int main() {
Id a, b, c;
std::cout << a.n() << b.n() << c.n() << ' ' << Id::count() << '\n';
}
T5. (operator and comparison)
#include <iostream>
class N {
public:
explicit N(int v) : v_(v) {}
N operator+(const N& o) const { return N{v_ + o.v_}; }
bool operator<(const N& o) const { return v_ < o.v_; }
int v() const { return v_; }
private:
int v_;
};
int main() {
N a{2}, b{5};
std::cout << (a + b).v() << ' ' << (a < b) << ' ' << (b < a) << '\n';
}
T6. (inheritance and virtual)
#include <iostream>
class Base {
public:
virtual void who() const { std::cout << "base "; }
virtual ~Base() = default;
};
class Derived : public Base {
public:
void who() const override { std::cout << "derived "; }
};
int main() {
Derived d;
Base& r = d;
r.who();
Base b = d; // note: by value
b.who();
}
T7. (slicing)
#include <iostream>
#include <string>
#include <vector>
class Animal { public: virtual std::string speak() const { return "..."; } virtual ~Animal() = default; };
class Dog : public Animal { public: std::string speak() const override { return "woof"; } };
int main() {
std::vector<Animal> zoo;
zoo.push_back(Dog{});
std::cout << zoo[0].speak() << '\n';
}
T8. (copy constructor)
#include <iostream>
class Buf {
public:
explicit Buf(int n) : data_(new int[n]) { data_[0] = 1; }
Buf(const Buf& o) : data_(new int[1]) { data_[0] = o.data_[0]; std::cout << "copy "; }
~Buf() { delete[] data_; }
int& at0() { return data_[0]; }
private:
int* data_;
};
int main() {
Buf a{2};
Buf b = a;
b.at0() = 9;
std::cout << a.at0() << ' ' << b.at0() << '\n';
}
T9. (move)
#include <iostream>
#include <utility>
class H {
public:
explicit H(int id) : id_(id) {}
H(H&& o) noexcept : id_(o.id_) { o.id_ = -1; std::cout << "mv "; }
~H() { if (id_ != -1) std::cout << "close" << id_ << ' '; }
int id() const { return id_; }
private:
int id_;
};
int main() {
H a{7};
H b = std::move(a);
std::cout << "b" << b.id() << " a" << a.id() << ' ';
}
T10. (exceptions)
#include <iostream>
#include <stdexcept>
int main() {
try {
try { throw std::out_of_range("x"); }
catch (const std::logic_error&) { std::cout << "logic "; }
} catch (const std::exception&) { std::cout << "outer "; }
std::cout << "end\n";
}
T11. (STL)
#include <algorithm>
#include <iostream>
#include <vector>
int main() {
std::vector<int> v{3, 1, 2, 2};
std::sort(v.begin(), v.end());
auto n = std::count_if(v.begin(), v.end(), [](int x) { return x > 1; });
std::cout << v.front() << v.back() << ' ' << n << '\n';
}
T12. (lambdas and captures)
#include <iostream>
int main() {
int base = 10;
auto add = [base](int x) { return base + x; };
base = 100;
std::cout << add(1) << ' ' << base << '\n';
}
Then: check your tracing against the answer key, and score yourself with Marking Yourself.