Why do we need virtual functions in C++?

前端 未结 26 3118
北恋
北恋 2020-11-21 05:50

I\'m learning C++ and I\'m just getting into virtual functions.

From what I\'ve read (in the book and online), virtual functions are functions in the base class that

相关标签:
26条回答
  • 2020-11-21 06:05

    Here is how I understood not just what virtual functions are, but why they're required:

    Let's say you have these two classes:

    class Animal
    {
        public:
            void eat() { std::cout << "I'm eating generic food."; }
    };
    
    class Cat : public Animal
    {
        public:
            void eat() { std::cout << "I'm eating a rat."; }
    };
    

    In your main function:

    Animal *animal = new Animal;
    Cat *cat = new Cat;
    
    animal->eat(); // Outputs: "I'm eating generic food."
    cat->eat();    // Outputs: "I'm eating a rat."
    

    So far so good, right? Animals eat generic food, cats eat rats, all without virtual.

    Let's change it a little now so that eat() is called via an intermediate function (a trivial function just for this example):

    // This can go at the top of the main.cpp file
    void func(Animal *xyz) { xyz->eat(); }
    

    Now our main function is:

    Animal *animal = new Animal;
    Cat *cat = new Cat;
    
    func(animal); // Outputs: "I'm eating generic food."
    func(cat);    // Outputs: "I'm eating generic food."
    

    Uh oh... we passed a Cat into func(), but it won't eat rats. Should you overload func() so it takes a Cat*? If you have to derive more animals from Animal they would all need their own func().

    The solution is to make eat() from the Animal class a virtual function:

    class Animal
    {
        public:
            virtual void eat() { std::cout << "I'm eating generic food."; }
    };
    
    class Cat : public Animal
    {
        public:
            void eat() { std::cout << "I'm eating a rat."; }
    };
    

    Main:

    func(animal); // Outputs: "I'm eating generic food."
    func(cat);    // Outputs: "I'm eating a rat."
    

    Done.

    0 讨论(0)
  • 2020-11-21 06:05

    The bottom line is that virtual functions make life easier. Let's use some of M Perry's ideas and describe what would happen if we didn't have virtual functions and instead only could use member-function pointers. We have, in the normal estimation without virtual functions:

     class base {
     public:
     void helloWorld() { std::cout << "Hello World!"; }
      };
    
     class derived: public base {
     public:
     void helloWorld() { std::cout << "Greetings World!"; }
     };
    
     int main () {
          base hwOne;
          derived hwTwo = new derived();
          base->helloWorld(); //prints "Hello World!"
          derived->helloWorld(); //prints "Hello World!"
    

    Ok, so that is what we know. Now let's try to do it with member-function pointers:

     #include <iostream>
     using namespace std;
    
     class base {
     public:
     void helloWorld() { std::cout << "Hello World!"; }
     };
    
     class derived : public base {
     public:
     void displayHWDerived(void(derived::*hwbase)()) { (this->*hwbase)(); }
     void(derived::*hwBase)();
     void helloWorld() { std::cout << "Greetings World!"; }
     };
    
     int main()
     {
     base* b = new base(); //Create base object
     b->helloWorld(); // Hello World!
     void(derived::*hwBase)() = &derived::helloWorld; //create derived member 
     function pointer to base function
     derived* d = new derived(); //Create derived object. 
     d->displayHWDerived(hwBase); //Greetings World!
    
     char ch;
     cin >> ch;
     }
    

    While we can do some things with member-function pointers, they aren't as flexible as virtual functions. It is tricky to use a member-function pointer in a class; the member-function pointer almost, at least in my practice, always must be called in the main function or from within a member function as in the above example.

    On the other hand, virtual functions, while they might have some function-pointer overhead, do simplify things dramatically.

    EDIT: There is another method which is similar by eddietree: c++ virtual function vs member function pointer (performance comparison) .

    0 讨论(0)
  • 2020-11-21 06:07

    Virtual Functions are used to support Runtime Polymorphism.

    That is, virtual keyword tells the compiler not to make the decision (of function binding) at compile time, rather postpone it for runtime".

    • You can make a function virtual by preceding the keyword virtual in its base class declaration. For example,

       class Base
       {
          virtual void func();
       }
      
    • When a Base Class has a virtual member function, any class that inherits from the Base Class can redefine the function with exactly the same prototype i.e. only functionality can be redefined, not the interface of the function.

       class Derive : public Base
       {
          void func();
       }
      
    • A Base class pointer can be used to point to Base class object as well as a Derived class object.

    • When the virtual function is called by using a Base class pointer, the compiler decides at run-time which version of the function - i.e. the Base class version or the overridden Derived class version - is to be called. This is called Runtime Polymorphism.
    0 讨论(0)
  • 2020-11-21 06:07

    Why do we need Virtual Methods in C++?

    Quick Answer:

    1. It provides us with one of the needed "ingredients"1 for object oriented programming.

    In Bjarne Stroustrup C++ Programming: Principles and Practice, (14.3):

    The virtual function provides the ability to define a function in a base class and have a function of the same name and type in a derived class called when a user calls the base class function. That is often called run-time polymorphism, dynamic dispatch, or run-time dispatch because the function called is determined at run time based on the type of the object used.

    1. It is the fastest more efficient implementation if you need a virtual function call 2.

    To handle a virtual call, one needs one or more pieces of data related to the derived object 3. The way that is usually done is to add the address of table of functions. This table is usually referred to as virtual table or virtual function table and its address is often called the virtual pointer. Each virtual function gets a slot in the virtual table. Depending of the caller's object (derived) type, the virtual function, in its turn, invokes the respective override.


    1.The use of inheritance, run-time polymorphism, and encapsulation is the most common definition of object-oriented programming.

    2. You can't code functionality to be any faster or to use less memory using other language features to select among alternatives at run time. Bjarne Stroustrup C++ Programming: Principles and Practice.(14.3.1).

    3. Something to tell which function is really invoked when we call the base class containing the virtual function.

    0 讨论(0)
  • 2020-11-21 06:09

    OOP Answer: Subtype Polymorphism

    In C++, virtual methods are needed to realise polymorphism, more precisely subtyping or subtype polymorphism if you apply the definition from wikipedia.

    Wikipedia, Subtyping, 2019-01-09: In programming language theory, subtyping (also subtype polymorphism or inclusion polymorphism) is a form of type polymorphism in which a subtype is a datatype that is related to another datatype (the supertype) by some notion of substitutability, meaning that program elements, typically subroutines or functions, written to operate on elements of the supertype can also operate on elements of the subtype.

    NOTE: Subtype means base class, and subtyp means inherited class.

    Further reading regarding Subtype Polymorphism

    • https://en.wikipedia.org/wiki/Subtyping
    • https://en.wikipedia.org/wiki/Polymorphism_(computer_science)#Subtyping

    Technical Answer: Dynamic Dispatch

    If you have a pointer to a base class, then the call of the method (that is declared as virtual) will be dispatched to the method of the actual class of the created object. This is how Subtype Polymorphism is realised is C++.

    Further reading Polymorphism in C++ and Dynamic Dispatch

    • http://www.cplusplus.com/doc/tutorial/polymorphism/
    • https://en.cppreference.com/w/cpp/language/virtual

    Implementation Answer: Creates vtable entry

    For each modifier "virtual" on methods, C++ compilers usually create an entry in the vtable of the class in which the method is declared. This is how common C++ compiler realise Dynamic Dispatch.

    Further reading vtables

    • https://en.wikipedia.org/wiki/Virtual_method_table

    Example Code

    #include <iostream>
    
    using namespace std;
    
    class Animal {
    public:
        virtual void MakeTypicalNoise() = 0; // no implementation needed, for abstract classes
        virtual ~Animal(){};
    };
    
    class Cat : public Animal {
    public:
        virtual void MakeTypicalNoise()
        {
            cout << "Meow!" << endl;
        }
    };
    
    class Dog : public Animal {
    public:
        virtual void MakeTypicalNoise() { // needs to be virtual, if subtype polymorphism is also needed for Dogs
            cout << "Woof!" << endl;
        }
    };
    
    class Doberman : public Dog {
    public:
        virtual void MakeTypicalNoise() {
            cout << "Woo, woo, woow!";
            cout << " ... ";
            Dog::MakeTypicalNoise();
        }
    };
    
    int main() {
    
        Animal* apObject[] = { new Cat(), new Dog(), new Doberman() };
    
        const   int cnAnimals = sizeof(apObject)/sizeof(Animal*);
        for ( int i = 0; i < cnAnimals; i++ ) {
            apObject[i]->MakeTypicalNoise();
        }
        for ( int i = 0; i < cnAnimals; i++ ) {
            delete apObject[i];
        }
        return 0;
    }
    

    Output of Example Code

    Meow!
    Woof!
    Woo, woo, woow! ... Woof!
    

    UML class diagram of code example

    0 讨论(0)
  • 2020-11-21 06:10

    I've my answer in form of a conversation to be a better read:


    Why do we need virtual functions?

    Because of Polymorphism.

    What is Polymorphism?

    The fact that a base pointer can also point to derived type objects.

    How does this definition of Polymorphism lead into the need for virtual functions?

    Well, through early binding.

    What is early binding?

    Early binding(compile-time binding) in C++ means that a function call is fixed before the program is executed.

    So...?

    So if you use a base type as the parameter of a function, the compiler will only recognize the base interface, and if you call that function with any arguments from derived classes, it gets sliced off, which is not what you want to happen.

    If it's not what we want to happen, why is this allowed?

    Because we need Polymorphism!

    What's the benefit of Polymorphism then?

    You can use a base type pointer as the parameter of a single function, and then in the run-time of your program, you can access each of the derived type interfaces(e.g. their member functions) without any issues, using dereferencing of that single base pointer.

    I still don't know what virtual functions are good for...! And this was my first question!

    well, this is because you asked your question too soon!

    Why do we need virtual functions?

    Assume that you called a function with a base pointer, which had the address of an object from one of its derived classes. As we've talked about it above, in the run-time, this pointer gets dereferenced, so far so good, however, we expect a method(== a member function) "from our derived class" to be executed! However, a same method(one that has a same header) is already defined in the base class, so why should your program bother to choose the other method? In other words I mean, how can you tell this scenario off from what we used to see normally happen before?

    The brief answer is "a Virtual member function in base", and a little longer answer is that, "at this step, if the program sees a virtual function in the base class, it knows(realizes) that you're trying to use polymorphism" and so goes to derived classes(using v-table, a form of late binding) to find that another method with the same header, but with -expectedly- a different implementation.

    Why a different implementation?

    You knuckle-head! Go read a good book!

    OK, wait wait wait, why would one bother to use base pointers, when he/she could simply use derived type pointers? You be the judge, is all this headache worth it? Look at these two snippets:

    //1:

    Parent* p1 = &boy;
    p1 -> task();
    Parent* p2 = &girl;
    p2 -> task();
    

    //2:

    Boy* p1 = &boy;
    p1 -> task();
    Girl* p2 = &girl;
    p2 -> task();
    

    OK, although I think that 1 is still better than 2, you could write 1 like this either:

    //1:

    Parent* p1 = &boy;
    p1 -> task();
    p1 = &girl;
    p1 -> task();
    

    and moreover, you should be aware that this is yet just a contrived use of all the things I've explained to you so far. Instead of this, assume for example a situation in which you had a function in your program that used the methods from each of the derived classes respectively(getMonthBenefit()):

    double totalMonthBenefit = 0;    
    std::vector<CentralShop*> mainShop = { &shop1, &shop2, &shop3, &shop4, &shop5, &shop6};
    for(CentralShop* x : mainShop){
         totalMonthBenefit += x -> getMonthBenefit();
    }
    

    Now, try to re-write this, without any headaches!

    double totalMonthBenefit=0;
    Shop1* branch1 = &shop1;
    Shop2* branch2 = &shop2;
    Shop3* branch3 = &shop3;
    Shop4* branch4 = &shop4;
    Shop5* branch5 = &shop5;
    Shop6* branch6 = &shop6;
    totalMonthBenefit += branch1 -> getMonthBenefit();
    totalMonthBenefit += branch2 -> getMonthBenefit();
    totalMonthBenefit += branch3 -> getMonthBenefit();
    totalMonthBenefit += branch4 -> getMonthBenefit();
    totalMonthBenefit += branch5 -> getMonthBenefit();
    totalMonthBenefit += branch6 -> getMonthBenefit();
    

    And actually, this might be yet a contrived example either!

    0 讨论(0)
提交回复
热议问题