Start learning free

LESSON 554 OF 568 · OBJECT-ORIENTED PROGRAMMING - CHALLENGES

Ticket Numbers in C++: A Static Counter Shared by All Objects

Practice O(1) Read the full explanation

A ticket machine gives every new ticket the next number: the first ticket gets 1, the next one 2, and so on. The counter belongs to the class, not to any one ticket: it is a static member of Ticket, one copy shared by all the tickets. Each ticket keeps its own number, copied from the counter when the ticket is created.

Implement the class Ticket:

  • Ticket(name) creates a ticket for name. It adds 1 to the class-level counter and gives the new ticket that value as its number. The ticket keeps its name and its number in two fields, name and number.
  • The static method issued() returns how many tickets were created since the counter was last reset.
  • The static method resetCounter() sets the counter back to 0, so the next ticket gets number 1 again.

For each test case, the test code calls resetCounter() through the class name, then creates one ticket per name, in order. Once all the tickets of the case exist, it prints each ticket as its name, a space, # and its number, one ticket per line, and then the value of issued() on a line of its own.

The starter code spells these names the way your language does: reset_counter in Python, Issued and ResetCounter in C#. In Python the counter is a class attribute and the two methods are marked @staticmethod; in Kotlin and Scala the counter and the two methods live in the companion object, as in the Static Members lesson.

 

Example 1:

Input:
    Ticket("Ann")
    Ticket("Bob")
    Ticket("Cy")
Output:
Ann #1
Bob #2
Cy #3
3
Explanation: resetCounter() set the counter to 0. Each new ticket adds 1 to it and keeps the new value,
so the tickets get 1, 2 and 3, and issued() returns 3.

Example 2:

Input:
    Ticket("Mia")
    Ticket("Mia")
Output:
Mia #1
Mia #2
2
Explanation: The number comes from the shared counter, not from the name, so the two tickets for Mia
get different numbers. The numbers start at 1 again because resetCounter() ran before this case.

Example 3:

Input:
    (no tickets)
Output:
0
Explanation: resetCounter() set the counter to 0 and no ticket was created, so issued() returns 0.

 

Constraints:

  • A test case creates from 0 to 1000 tickets.
  • A name is one word of 1 to 20 English letters. Two tickets can have the same name.
  • The tickets are printed only after all of them were created, so every ticket must store its own number: reading the shared counter when printing would give every ticket the last number.
STEP 1 OF 7
    Ctrl + Enter to check

    C++ · OBJECT-ORIENTED PROGRAMMING - CHALLENGES

    Ticket Numbers in C++, explained

    Problem overview

    A ticket machine gives every new ticket the next number: the first ticket gets 1, the next gets 2, and so on. The counter belongs to the class, not to any one ticket. In C++ that is a static data member of Ticket: one copy, shared by all tickets. Each ticket keeps its own number, copied from the counter when the ticket is created.

    The class has three parts:

    • Ticket(name) adds 1 to the class-level counter and gives the new ticket that value as its number. The ticket keeps its name and number in two fields.
    • The static method issued() returns how many tickets were created since the counter was last reset.
    • The static method resetCounter() sets the counter back to 0, so the next ticket gets number 1 again.

    Creating Ticket("Ann"), Ticket("Bob") and Ticket("Cy") after a reset gives them the numbers 1, 2 and 3, and issued() returns 3. A test case may create up to 1000 tickets, and two tickets can have the same name.

    Approach

    There are two kinds of data here. The name and the number differ from ticket to ticket, so they are ordinary fields: every object has its own copy. The counter is one value for the whole class, so it is a static member: there is exactly one, whichever tickets exist, and even when none exists.

    The constructor does the numbering in two steps: it increments the shared counter, then copies the counter's value into the new ticket's own number. The copy is the important part. The counter keeps moving as more tickets are made, but a ticket's number is a snapshot of the counter at the moment it was created, so it stays the same afterwards. Reading the shared counter later, for example when printing, would give every ticket the latest number.

    Both methods and the constructor do a constant amount of work, so creating a ticket costs O(1).

    Step by step

    1. Declare the counter as static int count; in the public: section. One copy serves the whole class.
    2. Declare the per-ticket fields, a string name and an int number, without static, because every ticket needs its own.
    3. Store the name in the constructor with this->name = name;. The parameter and the field have the same name, so this-> picks the field.
    4. Add 1 to the counter with count++. Inside the class you can write just count; outside it, the name is Ticket::count.
    5. Copy the counter into the ticket with this->number = count;. After a reset, the first ticket finds the counter at 1 here, so the numbers start at 1.
    6. issued() returns count. A static method belongs to the class and is called on the class name with no ticket at all, so it can use only static members. That is why it can't use this.
    7. resetCounter() assigns 0 to count. It runs before each test case, so every case numbers its tickets from 1 again.

    The solution in C++

    class Ticket {
    public:
        static int count;
    
        string name;
        int number;
    
        Ticket(string name) {
            this->name = name;
            count++;
            this->number = count;
        }
    
        static int issued() {
            return count;
        }
    
        static void resetCounter() {
            count = 0;
        }
    };
    
    int Ticket::count = 0;

    The line after the class

    The last line, int Ticket::count = 0;, is easy to miss, and the program doesn't link without it. Declaring static int count; inside the class only says that the member exists. The definition, which reserves the memory and gives the starting value, goes once outside the class, with the class name and :: in front. It is already in the lesson's starter code. Leaving it out gives a linker error such as undefined reference to `Ticket::count' as soon as anything uses the counter.

    Since C++17 there is another way: a static member declared inline can be given its value inside the class, with no definition outside.

    #include <iostream>
    #include <string>
    using namespace std;
    
    class Pass {
    public:
        inline static int count = 0;
        string name;
        int number;
        Pass(string name) {
            this->name = name;
            count++;
            this->number = count;
        }
    };
    
    int main() {
        Pass a("Ann");
        Pass b("Bob");
        cout << a.number << " " << b.number << " " << Pass::count << endl;
    }

    This prints:

    1 2 2

    A run

    The tests call resetCounter() through the class name before every case, then create the tickets. Here is the first example, followed by what happens to the numbers when the counter moves on:

    #include <iostream>
    #include <string>
    using namespace std;
    
    // the class above goes here
    int main() {
        Ticket::resetCounter();
        Ticket t1("Ann");
        Ticket t2("Bob");
        Ticket t3("Cy");
        cout << t1.name << " #" << t1.number << endl;
        cout << t2.name << " #" << t2.number << endl;
        cout << t3.name << " #" << t3.number << endl;
        cout << Ticket::issued() << endl;
        cout << Ticket::count << " " << t1.number << endl;
    }

    It prints:

    Ann #1
    Bob #2
    Cy #3
    3
    3 1

    The first four lines match the example. The last line shows the difference between the shared counter and a ticket's own number: count is 3, and t1 still has number 1. Reading t1.count works too, because every ticket sees the same shared member, but writing Ticket::count makes it clear that the value belongs to the class.

    After a reset the numbers start over, and tickets made earlier keep the numbers they already have:

    #include <iostream>
    #include <string>
    using namespace std;
    
    // the class above goes here
    int main() {
        Ticket::resetCounter();
        Ticket a("Mia");
        Ticket b("Mia");
        cout << a.number << " " << b.number << " " << Ticket::issued() << endl;
        Ticket::resetCounter();
        cout << Ticket::issued() << endl;
        Ticket c("Mia");
        cout << c.number << " " << a.number << " " << Ticket::issued() << endl;
    }

    It prints:

    1 2 2
    0
    1 1 1

    Common mistakes

    • Printing or storing count instead of number. Every ticket would then show the latest counter value, because the counter is shared.
    • Declaring number as static. All tickets would then share one number.
    • Assigning number before incrementing the counter, which gives the numbers 0, 1, 2 instead of 1, 2, 3.
    • Using this inside issued() or resetCounter(). A static method isn't called on an object, so there is no this, and the compiler rejects it.
    • Forgetting resetCounter(): a second case would continue numbering where the first one stopped.
    Keep going with C++500+ step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free