why do we actually have virtual functions?

限于喜欢 提交于 2019-12-03 02:39:11
pyon

ABSTRACT

In this paper, we discuss virtual functions in C++. Part zero explains how virtual functions are declared and overridden. Part one attempts (and perhaps fails) to explain how virtual functions are implemented. Part two is a sample program that uses the example classes defined in parts zero and one. Part three is the classic animal example given in every virtual function - polymorphism tutorial.

PART ZERO

A method of a class is said to be virtual if and only if its declared to be so.

class my_base
{
public:
            void non_virtual_test() { cout << 4 << endl; } // non-virtual
    virtual void virtual_test()     { cout << 5 << endl; } // virtual
};

(Of course, I am assuming the programmer did not previously do anything like #define virtual.)

A class that redeclares and re-implements a non-virtual method of one of its bases is said to overload that method. A class that redeclares and re-implements a virtual method of one of its bases is said to override that method.

class my_derived : public my_base
{
public:
    void non_virtual_test() { cout << 6 << endl; } // overloaded
    void virtual_test()     { cout << 7 << endl; } // overriden
};

PART ONE

When the compiler detects a class has virtual methods, it automatically adds a virtual method table (also known as vtable) to the class' memory layout. The result is similar to what would have been generated from compiling this code:

class my_base
{
//<vtable>
// The vtable is actually a bunch of member function pointers
protected:
    void (my_base::*virtual_test_ptr)();
//</vtable>

// The actual implementation of the virtual function
// is hidden from the rest of the program.
private:
    void virtual_test_impl() { cout << 5 << endl; }

// Initializing the real_virtual_test pointer in the vtable.
public:
    my_base() : virtual_test_ptr(&my_base::virtual_test_impl) {}

public:
    void non_virtual_test() { cout << 4 << endl; }
    // The interface of the virtual function is a wrapper
    // around the member function pointer.
    inline void virtual_test() { *virtual_test_ptr(); }
};

When the compiler detects a class has overridden a virtual method, it replaces its associated entry in the vtable. The result is similar to what would have been generated from compiling this code:

class my_derived : public my_base
{
// The actual implementation of the virtual function
// is hidden from the rest of the program.
private:
    void virtual_test_impl() { cout << 7 << endl; }

// Initializing the real_virtual_test pointer in the vtable.
public:
    my_derived() : virtual_test_ptr(&my_derived::virtual_test_impl) {}

public:
    void non_virtual_test() { cout << 6 << endl; }
};

PART TWO

Now that it's clear that virtual functions are implemented using vtables, which are nothing but a bunch of function pointers, it should be clear what this code does:

#include <iostream>

using namespace std;

class my_base
{
    public:
            void non_virtual_test() { cout << 4 << endl; }
    virtual void virtual_test()     { cout << 5 << endl; }
};

class my_derived : public my_base
{
public:
    void non_virtual_test() { cout << 6 << endl; }
    void virtual_test()     { cout << 7 << endl; }
}

int main()
{
    my_base* base_obj = new my_derived();

    // This outputs 4, since my_base::non_virtual_test() gets called,
    // not my_derived::non_virtual_test().
    base_obj->non_virtual_test();

    // This outputs 7, since the vtable pointer points to
    // my_derived::virtual_test(), not to my_base::virtual_test().
    base_obj->virtual_test();

    // We shall not forget
    // there was an object that was pointed by base_obj
    // who happily lived in the heap
    // until we killed it.
    delete base_obj;

    return 0;
}

PART THREE

Since no virtual function example is complete without an example with animals...

#include <iostream>

using namespace std;

class animal
{
public:
    virtual void say_something()
    { cout << "I don't know what to say." << endl
           << "Let's assume I can growl." << endl; }

    /* A more sophisticated version would use pure virtual functions:
     *
     * virtual void say_something() = 0;
     */
};

class dog : public animal
{
public:
    void say_something() { cout << "Barf, barf..." << endl; }
};

class cat : public animal
{
public:
    void say_something() { cout << "Meow, meow..." << endl; }
};

int main()
{
    animal *a1 = new dog();
    animal *a2 = new cat();
    a1->say_something();
    a2->say_something();
}
JonH

Virtual function / method is simply a function whose behavior can be overriden within a subclass (or in C++ terms a derived class) by redefining how the function works (using the same signature).

Think of a base class mammal with a speak function. The function is void and simply couts how a mammal speaks. When you inherit from this class you can override the speak method so that dogs go "Arf Arf!" and cats go "Meow Meow".

Your question seems to ask what are the differences, well there are none because with virtual functions one can override the behavior of these functions. You may be after the difference between overriding functions and overloading them.

Overloading functions means to create a function with the same name but different arguments i.e. different number- and type-of argument(s). Here is an explanation on overloading in C++ from IBM's site:

Overloading (C++ only) If you specify more than one definition for a function name or an operator in the same scope, you have overloaded that function name or operator. Overloaded functions and operators are described in Overloading functions (C++ only) and Overloading operators (C++ only), respectively.

An overloaded declaration is a declaration that had been declared with the same name as a previously declared declaration in the same scope, except that both declarations have different types.

If you call an overloaded function name or operator, the compiler determines the most appropriate definition to use by comparing the argument types you used to call the function or operator with the parameter types specified in the definitions. The process of selecting the most appropriate overloaded function or operator is called overload resolution, as described in Overload resolution (C++ only).

As for the full rational reason for situations where virtual functions are required, this blog post gives a good one: http://nrecursions.blogspot.in/2015/06/so-why-do-we-need-virtual-functions.html

The difference between function overriding and virtual function becomes important with polymorphism. Specifically when using references or pointers to a base class.

The Base Setup

In C++, any derived class can be passed to a function requiring a base class object. (See also Slicing and LSP). Given:

struct Base_Virtual
{
  virtual void some_virtual_function();
};

struct Base_Nonvirtual
{
  void some_function();
};

void Function_A(Base_Virtual * p_virtual_base);
void Function_B(Base_Nonvirtual * p_non_virtual_base);

In the above code, there are two base classes, one declares a virtual method, the other declares a non-virtual function.

Two functions are declared that require pointers to the respective base clases.

The Derived Classes

Let us now test the polymorphism, especially virtual vs. non-virtual (overriding methods). The structures:

struct Derived_From_Virtual
: public Base_Virtual
{
  void some_virtual_function(); // overrides Base_Virtual::some_virtual_function()
};

struct Derived_From_Nonvirtual : public Base_Nonvirtual { void some_function(); }

According to the C++ language, I can pass a pointer to a Derived_From_Virtual to Function_A because Derived_From_Virtual inherits from Base_Virtual. I can also pass a pointer to Derived_From_Nonvirtual to Function_B.

The Difference Between virtual and overriding

The virtual modifier in Base_Virtual, tells the compiler that Function_A will use Derived_From_Virtual::some_virtual_function() instead of the method in Base_Virtual. This is because the method is virtual, the final definition may reside in a future or derived class. The actual definition says to use the method in the most derived class containing the definition.

When passing a pointer to Derived_From_Nonvirtual to Function_B, the compiler will instruct the function to use the method of the base class, Base_Nonvirtual::some_function(). The some_function() method in the derived class is a separate, unrelated, method from the base class.

The primary difference between virtual and overriding occurs with polymorphism.

Check out C++ FAQ lite, http://www.parashift.com/c++-faq-lite/. is probably one of the best C++ resources for beginners. it has in-depth write-up about virtual functions and overriding.

I personally found C++ FAQ to be an excellent source as I learn C++. Other people have different opinion, your mileage may vary

David Rodríguez - dribeas

This is more a follow up on the comments from this answer than an answer by itself.

virtual is a keyword that requests runtime dispatch for the method being declared and at the same time declares the method as one of the overrides (implemented pure virtual methods aside). The method being declared, and any method that shares the exact signature and name in the deriving hierarchy from this class down are overrides. When you call a virtual method through a parent pointer or reference, the runtime will call the most derived override in the hierarchy of the called upon object.

When a method is not virtual, and the same method is defined later on in the hierarchy, you are hiding the parent method. The difference here is that when the method is being called through a base pointer or reference it will be calling the base implementation, while if it is being called in a derived object it will call the derived implementation. This, among other cases, is called hiding because the base and derived functions are unrelated, and having it defined in the derived class will hide the base version from a call:

struct base {
   virtual void override() { std::cout << "base::override" << std::endl; }
   void not_override() { std::cout << "base::not_override" << std::endl; }
};
struct derived : base {
   void override() { std::cout << "derived::override" << std::endl; }
   void not_override() { std::cout << "derived::not_override" << std::endl; }
};
int main() {
   derived d;
   base & b = d;

   b.override();     // derived::override
   b.not_override(); // base::not_override
   d.not_override(); // derived::not_override
}

The difference, and what is wrong in the answer by @erik2red is that overrides are intimately related to virtual functions and imply that there is a runtime dispatch mechanism in place that determines the most derived override to call. The behavior that is shown in the answer and associated to override is actually the behavior when there are no overrides but rather method hiding.

Other issues

The language allows for pure virtual methods with implementation. It does not say anything about what terminology should be used with them, but a pure virtual method will never be considered for runtime dispatch. The reason is that when a classes with pure virtual methods (even if implemented) are considered abstract classes, and you cannot instantiate an object of the class. Once you have a derived class that provides an implementation for that method, that implementation becomes the final override in the hierarchy. The class can now be instantiated, but the pure virtual method will not be called through the runtime dispatch mechanism.

Virtual methods that are not the final override, as well as hided methods can be called if using the fully qualified name. In the case of virtual methods, using the fully qualified name disables the polymorphic dispatch mechanism for the call: d.base::override() will call the base implementation even if there are other overrides in deriving classes.

A method can hide other methods in base classes even if the signatures do not match.

struct base {
   void f() {}
};
struct derived : base {
   void f(int) {}
};
int main() {
   derived d;
   // d.f() // error, derived::f requires an argument, base::f is hidden in this context
}

As with overrides, d.base::f() will call the base version, not because it disables polymorphism --it does not, as the method is not declared virtual it will never have polymorphic behavior-- but because the full qualification tells the compiler where the method is, even if it was hidden by another method in the derived class.

Virtual functions exist to help design the behavior of a base class. A base class of pure virtual functions cannot be instantiated and is called an abstract class.

It's up to the derived classes to implement those methods described by the virtual functions in the base class. Derived classes can then be instantiated (they exist and occupy memory).

Deriving from derived classes can redfine a function already defined in the parent object. This technique you already know as overriding and allows you to customize the behavior of this child object.

As you learn more C++, you'll find that inheritance isn't all that it's cracked up to be. Composition and is often a better alternative. Have fun.

When coming from Java, one might find the concept of virtual vs. non virtual member functions confusing. The thing to remember is that Java methods correspond to virtual member functions in C++.

The question is not so much why we actually have virtual functions, but why do we have non-virtual ones? The way I justify them to myself (correct me if I'm wrong) is that they are cheaper to implement, as calls to them can be resolved at compile time.

The classic example is that of a paint program where a base Shape class is created with a virtual draw() function. Then each of the shapes (circle, rectangle, triangle, etc) can be created as subclass that each implement their draw() function in the appropriate manner and the core paint program can keep a list of Shapes that will each do the appropriate draw() function even though only a pointer to the base Shape class is stored.

The difference is just used when you call the method of the derived class, through a pointer to a base class object. In that moment you if the method you're calling was override in the derived class, you'll get the exceution of the base class, instead if was virtual then you got the execution of the derived class method.

#include <iostream>

class A{
    public:
    virtual void getA() { std::cout << "A in base" << std::endl;};
};

class B : public A {
    public:
    void getA() { std::cout << "A in derived class" << std::endl;}
};

int main(int argc, char** argv)
{
    A a;
    B b;
    a.getA();
    b.getA();

    A* t = new B;
    t->getA();
}

For example: in this program t->getA() print "A in derived class", but if there'were no virtual modificator in the base class A, then it would print "A in base".

Hope it helps.

Helicopters and airplanes both fly, but they do it in different ways -- they are both instances of some hypothetical object Flyer . You can ask the Flyer object to "fly" -- but Flyer is just a interface It doesn't know anything about flying other than that it should be able to fly.

However if both the helicopter and airplane follow the interface of flyer, than if had an airfield object and you gave it a Flyer all the airfield needs to do is to request the flyers to fly.

For example:

Airplace X=Airplane X("boeing 747");
Airfield::takeoff(&X);

Helicopter Y= Helicopter("Comache");
Airfield::takeof(&Y);

void Airfield::takeOff(Flyer * f)
{
     f->fly();
}

C++ is a strict type-safe language, and this sort of functionality (making function calls to derived classes indirectly via a base class) is only possible when RTTI is enabled for the object hierarchy, and qualifying a member function virtual enables this.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!