December Code

Virtual functions interview questions, with answers

Virtual functions are how C++ decides at run time which version of a method to call, and they are the part of object-oriented programming where C++ and Java differ most. Interviewers who have moved past 'what is polymorphism' ask about the mechanism: when a call is bound, what a vtable is, and what happens in the corners — constructors, default arguments, and signatures that almost match.

Below are those questions with short C++ and Java answers. The polymorphism page covers why C++ needs the virtual keyword at all and why base classes need virtual destructors; this page picks up from there. Then take the free OOP diagnostic — ten questions drawn from all fifteen OOP topics — to see which concepts you can define but not apply.

The questions, with answers

  1. 1.When is a C++ member function call bound at compile time, and when at run time?

    In short: Only a virtual function called through a pointer or a reference is bound at run time; non-virtual calls, calls on an object by value and qualified calls are bound at compile time.

    Four cases cover it. A non-virtual function is always chosen from the static type, the type the compiler sees, which is why the diagnostic question on this page prints the base class's text. A virtual function called through a pointer or a reference is chosen from the dynamic type, the object actually there. A virtual function called on an object directly, such as circle.area() where circle is declared as a Circle, resolves to Circle's version, and compilers bind it statically, because the object cannot be anything but a Circle. And a qualified call such as Shape::area() deliberately asks for one version and skips dispatch; that is how an override calls the base version. Interviewers like the third case, because candidates who memorised 'virtual means run time' get it wrong.

  2. 2.How do the vtable and vptr make a virtual call work, and what does it cost?

    In short: Each class with virtual functions has a table of function pointers, each object carries a hidden pointer to its class's table, and a virtual call is an indirect call through it.

    The compiler gives every polymorphic class a vtable with one slot per virtual function, filled with that class's versions, and adds a hidden vptr to every object, set by the constructor to point at the right table. A call like p->area() becomes: read p's vptr, read the slot for area, call the address found there. The costs are one pointer per object, one small table per class, and an indirect call the compiler usually cannot inline, which matters in a tight loop over millions of small objects and almost nowhere else. The C++ standard does not require vtables, only the behaviour, but every mainstream compiler implements virtual calls this way, so it is the answer interviewers expect.

  3. 3.What happens when a constructor calls a virtual function?

    In short: In C++ the call runs the version belonging to the class being constructed, never a derived one; Java runs the derived version, before the derived class's fields are initialised.

    While a Base constructor runs, the Derived part of the object does not exist yet, so C++ treats the object as a Base: a virtual call from inside Base's constructor runs Base's version, and calling a pure virtual function that way is undefined behaviour. The same holds in destructors, in reverse. Java makes the opposite choice and dispatches to the override, which is arguably worse: the override runs before the subclass's field initialisers have executed, so it sees null and zero, and the Java code below prints 'name = null'. The advice is the same in both languages: do not call overridable methods from a constructor.

    class Base {
        Base() { describe(); }                 // runs Derived.describe() in Java
        void describe() { System.out.println("base"); }
    }
    
    class Derived extends Base {
        private String name = "derived";       // not yet assigned when Base() runs
        @Override void describe() { System.out.println("name = " + name); }
    }
    
    // new Derived() prints: name = null
  4. 4.Why do default arguments on virtual functions surprise people?

    In short: Default arguments come from the static type of the pointer while the function body comes from the dynamic type, so a derived body can run with the base's default.

    In the code below, p points to a Derived, so p->show() runs Derived::show — dispatch works as usual — but the argument is filled in at compile time from the declaration the compiler sees through a Base pointer, so x is 1, not 2. The output is 'Derived 1', a combination neither author intended. The standard says exactly this: a virtual function call uses the default arguments of the declaration in the static type. The practical rules follow from it: never give an override a different default from the base, or avoid default arguments on virtual functions altogether and use a non-virtual overload that calls the virtual one.

    struct Base {
        virtual void show(int x = 1) { std::cout << "Base " << x; }
        virtual ~Base() = default;
    };
    struct Derived : Base {
        void show(int x = 2) override { std::cout << "Derived " << x; }
    };
    
    int main() {
        Base* p = new Derived();
        p->show();       // prints "Derived 1"
        delete p;
    }
  5. 5.What do the override and final specifiers do?

    In short: override makes the compiler check that a function really overrides a base virtual function; final forbids overriding a function any further, or deriving from a class at all.

    Without override, a small signature mismatch silently creates a new function instead of an override: in the code below the base draw is const and the derived one is not, so without the specifier Derived::draw would be an unrelated function and a call through a Base pointer would still run Base::draw. With override, that mismatch is a compile error at the line where the mistake is. final works the other way: on a function it stops overriding in further subclasses, and on a class it stops derivation entirely, which also lets the compiler call the function directly instead of through the vtable. Both arrived in C++11, and the C++ Core Guidelines recommend marking every overriding function with exactly one of virtual, override or final.

    struct Base {
        virtual void draw() const;
        virtual void resize(int factor);
        virtual ~Base() = default;
    };
    struct Derived : Base {
        void draw() override;               // error: no 'const', so it overrides nothing
        void resize(int factor) final;      // overrides; no class below may override it again
    };
  6. 6.What is a pure virtual function, and can it have a body?

    In short: A pure virtual function, declared with = 0, needs no implementation of its own and makes its class abstract; it can still be given a body, which derived classes call explicitly.

    Writing virtual double area() const = 0; says that Shape has no meaningful area of its own, so Shape cannot be instantiated and every concrete subclass must override area. Less well known: a pure virtual function may still be defined, outside the class, and an override can call it as Shape::area() to reuse common behaviour while still forcing each subclass to make the decision. One case requires a body: a pure virtual destructor, used to make a class abstract when it has no other natural pure function, must be defined, because every derived destructor calls it. A class stays abstract until every pure virtual function it inherits has an overrider.

    struct Shape {
        virtual double area() const = 0;    // pure: Shape is abstract
        virtual ~Shape() = 0;               // a pure virtual destructor...
    };
    Shape::~Shape() {}                      // ...still needs a definition
    
    double Shape::area() const { return 0.0; }   // optional body, called as Shape::area()
  7. 7.Are Java methods virtual, and how do you stop one being overridden?

    In short: Every Java instance method is virtual by default; private and static methods are bound at compile time, and marking a method final stops it being overridden.

    Java has no virtual keyword because it has no choice to make: calling an instance method always dispatches on the object's actual class. The exceptions are the methods that cannot be overridden in the first place. A private method is invisible to subclasses. A static method belongs to the class, so a subclass static method with the same signature hides the parent's instead of overriding it, and which one runs depends on the declared type of the variable, as the example shows. A final method cannot be overridden and a final class cannot be extended, Java's counterpart to C++'s final, while the @Override annotation plays the role of C++'s override.

    class Parent {
        static String kind() { return "parent static"; }
        String name() { return "parent instance"; }
    }
    class Child extends Parent {
        static String kind() { return "child static"; }      // hides, does not override
        @Override String name() { return "child instance"; }
    }
    
    Parent p = new Child();
    // p.name() returns "child instance"  (dispatched on the object)
    // p.kind() returns "parent static"   (bound to the declared type, Parent)
  8. 8.How do you decide whether a C++ member function should be virtual?

    In short: Make it virtual only when derived classes are meant to change that behaviour; many designs keep public functions non-virtual and route them to private virtual hooks.

    Virtual is a promise that subclasses may replace the behaviour, so it belongs on functions designed to be replaced, and a class with any virtual function should also have a virtual destructor, as the polymorphism page explains. The non-virtual interface idiom takes this further: the public function is non-virtual and does the fixed work — checking arguments, locking, logging — and then calls a private virtual function that subclasses override for the part that varies. The base class keeps control of the contract, and a subclass cannot skip the checks. It is the template method pattern expressed with access control, and naming it is a good way to finish an answer about virtual functions.

How the diagnostic asks it

One question from the OOP bank, exactly as a sitting would show it. The bank has 4 on virtual functions & dynamic binding and 60 across OOP.

Virtual Functions & Dynamic Binding · mediumOOP-017

Consider this C++ code:

class Base {
public:
    void greet() { std::cout << "Base"; }
};
class Derived : public Base {
public:
    void greet() { std::cout << "Derived"; }
};

int main() {
    Base* ptr = new Derived();
    ptr->greet();
}

What is printed, and why?

  1. 1"BaseDerived" -- because both versions execute in sequence.
  2. 2"Base" -- because greet() is not declared virtual, so the call is resolved at compile time based on the pointer's static type (Base).correct
  3. 3The code fails to compile because Derived redefines greet() without the virtual keyword.
  4. 4"Derived" -- because C++ always calls the most-derived version of a method regardless of virtual.

Since greet() isn't declared virtual in Base, the compiler uses static binding based on the pointer's declared type (Base*), so ptr->greet() calls Base::greet(), printing "Base" -- this is a classic pitfall showing why virtual is needed for runtime polymorphism. Believing C++ always calls the most-derived version is a common misconception: C++ does not default to dynamic dispatch. Both versions never execute in sequence, and redefining a non-virtual method in a derived class is legal, so the code compiles.

Measure it

Reading answers tells you what’s true. A diagnostic tells you what you get wrong.

10 OOP questions across its topics, easy to hard, about fifteen minutes. You get a readiness figure with the arithmetic shown, the topics you missed named, and a practice set sized for today. Free: 1 diagnostic a month and 15 problems a day. No card.

What the readiness test measures · how the score is computed

By Harshit · updated