Why do we need virtual functions in C++?

前端 未结 26 3587
北恋
北恋 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:10

    The problem with explanations to virtual functions, is that they don't explain how it is used in practice, and how it helps with maintainability. I've created a virtual function tutorial which people have already found very useful. Plus, it's based on a battlefield premise, which makes it a bit more exciting: https://nrecursions.blogspot.com/2015/06/so-why-do-we-need-virtual-functions.html.

    Consider this battlefield application:

    #include "iostream"
    
    //This class is created by Gun1's company
    class Gun1 {public: void fire() {std::cout<<"gun1 firing now\n";}};
    //This class is created by Gun2's company
    class Gun2 {public: void shoot() {std::cout<<"gun2 shooting now\n";}};
    
    //We create an abstract class to interface with WeaponController
    class WeaponsInterface {
     public:
     virtual void shootTarget() = 0;
    };
    
    //A wrapper class to encapsulate Gun1's shooting function
    class WeaponGun1 : public WeaponsInterface {
     private:
     Gun1* g;
    
     public:
     WeaponGun1(): g(new Gun1()) {}
     ~WeaponGun1() { delete g;}
     virtual void shootTarget() { g->fire(); }
    };
    
    //A wrapper class to encapsulate Gun2's shooting function
    class WeaponGun2 : public WeaponsInterface {
     private:
     Gun2* g;
    
     public:
     WeaponGun2(): g(new Gun2()) {}
     ~WeaponGun2() { delete g;}
     virtual void shootTarget() { g->shoot(); }
    };
    
    class WeaponController {
     private:
     WeaponsInterface* w;
     WeaponGun1* g1;
     WeaponGun2* g2;
     public:
     WeaponController() {g1 = new WeaponGun1(); g2 = new WeaponGun2(); w = g1;}
     ~WeaponController() {delete g1; delete g2;}
     void shootTarget() { w->shootTarget();}
     void changeGunTo(int gunNumber) {//Virtual functions makes it easy to change guns dynamically
       switch(gunNumber) {
         case 1: w = g1; break;
         case 2: w = g2; break;
       }
     }
    };
    
    
    class BattlefieldSoftware {
     private:
     WeaponController* wc;
     public:
     BattlefieldSoftware() : wc(new WeaponController()) {}
     ~BattlefieldSoftware() { delete wc; }
    
     void shootTarget() { wc->shootTarget(); }
     void changeGunTo(int gunNumber) {wc->changeGunTo(gunNumber); }
    };
    
    
    int main() {
     BattlefieldSoftware* bf = new BattlefieldSoftware();
     bf->shootTarget();
     for(int i = 2; i > 0; i--) {
         bf->changeGunTo(i);
         bf->shootTarget();
     }
     delete bf;
    }
    

    I encourage you to first read the post on the blog to get the gist of why the wrapper classes were created.

    As visible in the image, there are various guns/missiles that can be connected to a battlefield software, and commands can be issued to those weapons, to fire or re-calibrate etc. The challenge here is to be able to change/replace the guns/missiles without having to make changes to the blue battlefield software, and to be able to switch between weapons during runtime, without having to make changes in the code and re-compile.

    The code above shows how the problem is solved, and how virtual functions with well-designed wrapper classes can encapsulate functions and help in assigning derived class pointers during runtime. The creation of class WeaponGun1 ensures that you've completely separated the handling of Gun1 into the class. Whatever changes you do to Gun1, you'll only have to make changes in WeaponGun1, and have the confidence that no other class is affected.

    Because of WeaponsInterface class, you can now assign any derived class to the base class pointer WeaponsInterface and because it's functions are virtual, when you call WeaponsInterface's shootTarget, the derived class shootTarget gets invoked.

    Best part is, you can change guns during runtime (w=g1 and w=g2). This is the main advantage of virtual functions and this is why we need virtual functions.

    So no more necessity to comment out code in various places when changing guns. It's now a simple and clean procedure, and adding more gun classes is also easier because we just have to create a new WeaponGun3 or WeaponGun4 class and we can be confident that it won't mess up BattlefieldSoftware's code or WeaponGun1/WeaponGun2's code.

提交回复
热议问题