Skip to content

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.