Thread: Help understanding virtual and how it's used

  1. #1
    Registered User
    Join Date
    Feb 2015
    Posts
    56

    Help understanding virtual and how it's used

    Hi, I have been trying to learn classes and I am on virtual functions. I learned that virtual functions allow you to redefine functions in a child class without knowing which object you have at runtime, and that those virtual functions are populated in an array list called a vtable. I have some questions though, I have a difficult time comprehending things people tell me sometimes, if you could please explain it in laymans terms it would help tremendously:

    Say we have this code:

    Code:
    #include <iostream>
    class Base
    {
        public:
        virtual void speak()
        {
            std::cout << "I am base!\n";
        }
    };
    
    class Derived : public Base
    {
        public:    
        void speak()
        {
            std::cout << "I am derived!\n";
        }
    };
    
    int main()
    {
        Base baseObject;
        baseObject.speak();
        
        Derived derivedObject;
        derivedObject.speak();
        
        Base* pointer_to_base = &baseObject;
        pointer_to_base->speak();
        
        pointer_to_base = &derivedObject;
        pointer_to_base->speak();  
    }
    Now from what im told, in this code the pointer calls the most derived object, what exactly does that mean? From what i've observed if I do this:

    Code:
    Base* pointer_to_base = &baseObject;
        pointer_to_base->speak();
    
        pointer_to_base = &derivedObject;
        pointer_to_base->speak(); 
    The output is:

    I am base!
    I am derived

    but if I do this:

    Code:
    Base* pointer_to_base = &baseObject;
    pointer_to_base = &derivedObject;
    pointer_to_base->speak();
    
    pointer_to_base->speak(); 
    I get:

    I am base!
    I am base!

    So i assume that "most derived" meas the most current call to that virtual object, right?

    Also why do we have to use a pointer with the virtual functions? is it because we have to access the vtable and the only way to do that is with a pointer?
    Last edited by CH1156; 08-18-2018 at 06:01 AM.

  2. #2
    Guest
    Guest
    Quote Originally Posted by CH1156 View Post
    but if I do this:

    Code:
    Base* pointer_to_base = &baseObject;
    pointer_to_base = &derivedObject;
    pointer_to_base->speak();
    
    pointer_to_base->speak(); 
    I get:

    I am base!
    I am base!
    That shouldn't be the case. Your pointer points to the derivedObject before any call to speak, so you should see calls to its virtual method. Are you sure, you don't have a typo in your original code?
    Also why do we have to use a pointer with the virtual functions? is it because we have to access the vtable and the only way to do that is with a pointer?
    Code:
    Base baseObj;
    Derived derivedObj;
    baseObj = derivedObj; // Program _has to_ store a baseObj, so derivedObj gets "sliced" down, information is lost
    
    Base* bPtr;
    bPtr = &derivedObj; // Pointer just points to address of derivedObj, which itself is preserved, along with its vtable

  3. #3
    Registered User
    Join Date
    Feb 2015
    Posts
    56
    Code:
    That shouldn't be the case. Your pointer points to the derivedObject before any call to speak, so you should see calls to its virtual method. Are you sure, you don't have a typo in your original code?
    Yup, I tested it right before I posted it and thats what I got. Here is the same code just changed the names of some stuff and added a few things, I used guns as its easier to follow than using obj1 obj2:

    Code:
    #include <iostream>class Weapon
    {
        public:
        virtual void Fire()
        {
            std::cout << "Bang!!\n";
        };
    };
    
    
    class Handgun : public Weapon
    {
        public:
        void Fire()
        {
            std::cout << "9mm handgun!\n";
        }
    };
    
    
    class Shotgun : public Weapon
    {
        public:
        void Fire()
        {
            std::cout << "12 Guage shotgun!\n";
        }
    };
    
    
    class AK47 : public Weapon
    {
        public:
        void Fire()
        {
            std::cout << "7.62mm AK-47!\n";
        }
    };
    
    
    
    
    int main()
    {
        Weapon weapon;
        Handgun handgun;
        Shotgun shotgun;
        AK47 ak47;
    
    
        Weapon* p_weapon = &weapon;
    
    
        p_weapon = &handgun;
        p_weapon->Fire();
    
    
        p_weapon = &shotgun;
        p_weapon->Fire();
    
    
        p_weapon = &ak47;
        p_weapon->Fire();
    }
    and If i do:


    Code:
        Weapon weapon;    Handgun handgun;
        Shotgun shotgun;
        AK47 ak47;
    
    
        Weapon* p_weapon = &weapon;
    
    
        p_weapon = &handgun;
        p_weapon = &shotgun;
        p_weapon->Fire();
        p_weapon->Fire();
    
    
        p_weapon = &ak47;
        p_weapon->Fire();
    That gives me the shotgun output so im not sure whats going on in the first post.


    So basically a way to think of virtual functions is like a telephone switchboard that when called the compiler "Plugs in" the correct function with the object called?

    bPtr = &derivedObj; // Pointer just points to address of derivedObj, which itself is preserved, along with its vtable
    So every virtual function has its own vtable? or a vtable is created that stores all pointers to virtual objects? What exactly do you mean by its preserved?
    Last edited by CH1156; 08-18-2018 at 07:32 AM.

  4. #4
    Guest
    Guest
    What is the output of the following? I've added comments to clarify what should be happening.
    Code:
    int main()
    {
        Weapon weapon;
        Handgun handgun;
        Shotgun shotgun;
        AK47 ak47;
    
        Weapon* p_weapon = &weapon;
    
        p_weapon = &handgun;
        p_weapon->Fire(); // Handgun::Fire()
    
    
        p_weapon = &shotgun;
        p_weapon->Fire(); // Shotgun::Fire()
    
    
        p_weapon = &ak47;
        p_weapon->Fire(); // AK47::Fire()
    
        // ---------
    
        p_weapon = &handgun; // p_weapon points to Handgun
        p_weapon = &shotgun; // p_weapon now points to Shotgun
        p_weapon->Fire(); // Shotgun::Fire()
        p_weapon->Fire(); // Shotgun::Fire()
    
    
        p_weapon = &ak47; // p_weapon now points to AK47
        p_weapon->Fire(); // AK47::Fire()
    }
    It should be:
    Code:
    9mm handgun!
    12 Guage shotgun!
    7.62mm AK-47!
    // --------
    12 Guage shotgun!
    12 Guage shotgun!
    7.62mm AK-47!
    So basically a way to think of virtual functions is like a telephone switchboard that when called the compiler "Plugs in" the correct function with the object called?
    Right, the correct call is embedded in the given object, making it larger than without any virtual dispatch. If you have a function void foo(Base* bp) the compiler cannot necessarily know in advance whether that function will handle a Base object or something derived from it. The vtable pointer inside the passed object will inform the function which virtual method to call.

    So every virtual function has its own vtable? or a vtable is created that stores all pointers to virtual objects?
    Every class has its own vtable, every created object has a pointer to the corresponding vtable. So there's 1 vtable for Base, and 1 vtable for Dervied, but as many vtable-pointers as there are Base and Derived objects. At least that's the way I understand it.

  5. #5
    Registered User
    Join Date
    Feb 2015
    Posts
    56
    Ok, thank you, I understand virtual a lot better, but what about pure virtual? All it does is just FORCE child classes to define the pure virtual function? I keep getting an error saying it cant declare variable of abstract type.

    Code:
    #include <iostream>class Weapon
    {
        public:
        virtual void Fire() = 0;
    };
    
    
    class Handgun : public Weapon
    {
        public:
        void Fire() override
        {
            std::cout << "9mm handgun!\n";
        }
    };
    
    
    class Shotgun : public Weapon
    {
        public:
        void Fire() override
        {
            std::cout << "12 Guage shotgun!\n";
        }
    };
    
    
    class AK47 : public Weapon
    {
        public:
        void Fire() override
        {
            std::cout << "7.62mm AK-47!\n";
        }
    };
    
    
    void DischargeWeapon(Weapon& weapon)
    {
        Weapon* p_weapon = &weapon;
        p_weapon->Fire();
    }
    
    
    int main()
    {
        Weapon weapon;
        Handgun handgun;
        Shotgun shotgun;
        AK47 ak47;
    
    
        DischargeWeapon(ak47);
        DischargeWeapon(handgun);
        DischargeWeapon(shotgun);
    }
    Last edited by CH1156; 08-18-2018 at 08:19 AM.

  6. #6
    Guest
    Guest
    Ok, thank you, I understand virtual a lot better, but what about pure virtual? All it does is just FORCE child classes to define the pure virtual function?
    Right, the idea is that you have a pure base class that serves as an interface / layout that is to be implemented by inheriting classes. E.g. "This is what a Weapon should look like", without having to create a concrete BaseWeapon. I haven't really used it myself, so maybe someone else can chime in.

    I keep getting an error saying it cant declare variable of abstract type.
    That should be due to line #47. The compiler doesn't like you creating an instance of the abstract Weapon class. Remove that statement and things should work. I don't know if the C++ standard actually enforces this rule, or if it's up the compiler developers.

    Btw, you can simplify the DischargeWeapon function, as references also offer the necessary indirection that pointers do:
    Code:
    void DischargeWeapon(Weapon& weapon)
    {
        weapon.Fire();
    }
    Last edited by Guest; 08-18-2018 at 08:43 AM.

  7. #7
    Registered User
    Join Date
    Feb 2015
    Posts
    56
    Awesome thank you!

  8. #8
    Registered User
    Join Date
    Feb 2015
    Posts
    56
    Oh I had another question about classes in general, its the constructor. The newer versions of C++ allow you to initialize variables right in the class without the constructor:

    Code:
    class Weapon{
        public:
            virtual void Fire() = 0;
            virtual void Reload();
            
        private:
            std::string m_name = {};
            int m_magazineMaxSize = {};
    };
    As opposed to having to do it in the constructor:

    Code:
    class Weapon{
        public:
            Weapon(std::string weaponName, int magazineMaxSize) : m_name(weaponName), m_magazineMaxSize(magazineMaxSize) {}
            ~Weapon();
            virtual void Fire() = 0;
            virtual void Reload();
    
    
        private:
            std::string m_name = {};
            int m_magazineMaxSize = {};
    };
    I was just wondering what the difference is, and which way is better. Also by doing that in the constructor im getting tons of errors, im awful at deciphering compiler errors:

    ||=== Build: Debug in Test Class (compiler: GNU GCC Compiler) ===|
    C:\Users\Chay\Desktop\Work\C++\Test Class\main.cpp||In function 'int main()':|
    C:\Users\Chay\Desktop\Work\C++\Test Class\main.cpp|70|error: use of deleted function 'Handgun::Handgun()'|
    C:\Users\Chay\Desktop\Work\C++\Test Class\main.cpp|16|note: 'Handgun::Handgun()' is implicitly deleted because the default definition would be ill-formed:|
    C:\Users\Chay\Desktop\Work\C++\Test Class\main.cpp|16|error: no matching function for call to 'Weapon::Weapon()'|
    C:\Users\Chay\Desktop\Work\C++\Test Class\main.cpp|6|note: candidate: Weapon::Weapon(std::__cxx11::string, int)|
    C:\Users\Chay\Desktop\Work\C++\Test Class\main.cpp|6|note: candidate expects 2 arguments, 0 provided|
    C:\Users\Chay\Desktop\Work\C++\Test Class\main.cpp|3|note: candidate: Weapon::Weapon(const Weapon&)|
    C:\Users\Chay\Desktop\Work\C++\Test Class\main.cpp|3|note: candidate expects 1 argument, 0 provided|
    C:\Users\Chay\Desktop\Work\C++\Test Class\main.cpp|71|error: use of deleted function 'Shotgun::Shotgun()'|
    C:\Users\Chay\Desktop\Work\C++\Test Class\main.cpp|30|note: 'Shotgun::Shotgun()' is implicitly deleted because the default definition would be ill-formed:|
    C:\Users\Chay\Desktop\Work\C++\Test Class\main.cpp|30|error: no matching function for call to 'Weapon::Weapon()'|
    C:\Users\Chay\Desktop\Work\C++\Test Class\main.cpp|6|note: candidate: Weapon::Weapon(std::__cxx11::string, int)|
    C:\Users\Chay\Desktop\Work\C++\Test Class\main.cpp|6|note: candidate expects 2 arguments, 0 provided|
    C:\Users\Chay\Desktop\Work\C++\Test Class\main.cpp|3|note: candidate: Weapon::Weapon(const Weapon&)|
    C:\Users\Chay\Desktop\Work\C++\Test Class\main.cpp|3|note: candidate expects 1 argument, 0 provided|
    C:\Users\Chay\Desktop\Work\C++\Test Class\main.cpp|72|error: use of deleted function 'AK47::AK47()'|
    C:\Users\Chay\Desktop\Work\C++\Test Class\main.cpp|44|note: 'AK47::AK47()' is implicitly deleted because the default definition would be ill-formed:|
    C:\Users\Chay\Desktop\Work\C++\Test Class\main.cpp|44|error: no matching function for call to 'Weapon::Weapon()'|
    C:\Users\Chay\Desktop\Work\C++\Test Class\main.cpp|6|note: candidate: Weapon::Weapon(std::__cxx11::string, int)|
    C:\Users\Chay\Desktop\Work\C++\Test Class\main.cpp|6|note: candidate expects 2 arguments, 0 provided|
    C:\Users\Chay\Desktop\Work\C++\Test Class\main.cpp|3|note: candidate: Weapon::Weapon(const Weapon&)|
    C:\Users\Chay\Desktop\Work\C++\Test Class\main.cpp|3|note: candidate expects 1 argument, 0 provided|
    ||=== Build failed: 6 error(s), 0 warning(s) (0 minute(s), 0 second(s)) ===|
    Last edited by CH1156; 08-18-2018 at 09:49 AM.

  9. #9
    Registered User
    Join Date
    Feb 2015
    Posts
    56
    On top of that, I wanted to add the Discharge weapon and reload weapon fucntions to Weapon but how do I use them if I cant create an object of it in main?

    Code:
    #include <iostream>
    
    class Weapon
    {
        public:
            virtual void Fire() = 0;
            virtual void Reload()
            {
                std::cout << "Reloaded The weapon\n";
            }
    
    
        private:
            std::string m_name = {};
            int m_magazineMaxSize = {};
    };
    
    
    
    
    class Handgun : public Weapon
    {
        public:
            void Fire() override
            {
                std::cout << "9mm handgun!\n";
            }
    
    
            void Reload() override
            {
                std::cout << "Handgun reloaded!\n";
            }
    };
    
    
    class Shotgun : public Weapon
    {
        public:
            void Fire() override
            {
                std::cout << "12 Guage shotgun!\n";
            }
    
    
            void Reload() override
            {
                std::cout << "Shotgun reloaded!\n";
            }
    };
    
    
    class AK47 : public Weapon
    {
        public:
            void Fire() override
            {
                std::cout << "7.62mm AK-47!\n";
            }
    
    
            void Reload() override
            {
                std::cout << "AK47 reloaded\n";
            }
    };
    
    
    void DischargeWeapon(Weapon& weapon)
    {
        weapon.Fire();
    }
    
    
    void ReloadWeapon(Weapon& weapon)
    {
        weapon.Reload();
    }
    
    
    int main()
    {
        Handgun handgun;
        Shotgun shotgun;
        AK47 ak47;
    
    
        DischargeWeapon(handgun);
        ReloadWeapon(handgun);
    }

  10. #10
    Registered User
    Join Date
    Feb 2015
    Posts
    56
    So i've been playing with the code for a while now and doing some researcha nd I found this method works, but it seems tedious to do and fix, is there a simpler way?

    Code:
    #include <iostream>
    
    class Entity
    {
        public:
            Entity(std::string name, int health, int damage) : m_name(name), m_health(health), m_damage(damage){}
            virtual ~Entity() {}
    
    
        protected:
            std::string m_name = {};
            int m_health = {};
            int m_damage = {};
    };
    
    
    class Weapon : public Entity
    {
        public:
            Weapon(std::string name, int health, int damage)
                : Entity(name, health, damage){}
    
    
            virtual ~Weapon() {}
    
    
            virtual void Fire() = 0;
            virtual void Reload()
            {
                std::cout << "Reloaded weapon\n";
            }
    
    
        protected:
            int m_magazineMaxSize = {};
    };
    
    
    
    
    class Handgun : public Weapon
    {
        public:
            Handgun(std::string name, int health, int damage)
                : Weapon(name, health, damage){}
    
    
            void Fire() override
            {
                std::cout << "9mm " << m_name << "!\n";
                std::cout << "Health is: " << m_health << "\n";
                std::cout << "Damage this weapon does: " << m_damage << "\n";
            }
    
    
            void Reload() override
            {
                std::cout << m_name << " reloaded!\n";
            }
    };
    
    
    class Shotgun : public Weapon
    {
        public:
            Shotgun(std::string name, int health, int damage) : Weapon(name, health, damage){}
            void Fire() override
            {
                std::cout << "12 Guage " << m_name << "\n";
                std::cout << "Health is: " << m_health << "\n";
                std::cout << "Damage this weapon does: " << m_damage << "\n";
            }
    
    
            void Reload() override
            {
                std::cout << m_name << " reloaded!\n";
            }
    };
    
    
    void DischargeWeapon(Weapon& weapon)
    {
        weapon.Fire();
    }
    
    
    void ReloadWeapon(Weapon& weapon)
    {
        weapon.Reload();
    }
    
    
    int main()
    {
        Handgun handgun("Handgun", 5, 10);
        Shotgun shotgun("Shotgun", 10, 30);
    
    
    
    
        DischargeWeapon(handgun);
        ReloadWeapon(handgun);
    
    
        std::cout << "\n";
    
    
        DischargeWeapon(shotgun);
        ReloadWeapon(shotgun);
    }

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Queries regarding virtual pointer and virtual function
    By gaurav# in forum C++ Programming
    Replies: 2
    Last Post: 12-28-2016, 04:06 AM
  2. Trouble understanding virtual destructor example?
    By garmbrust in forum C++ Programming
    Replies: 5
    Last Post: 04-24-2015, 07:59 PM
  3. Pure Virtual Function - Virtual Method Table
    By u_peerless in forum C++ Programming
    Replies: 8
    Last Post: 06-07-2008, 01:19 AM
  4. Virtual functions but non-virtual destructor
    By cunnus88 in forum C++ Programming
    Replies: 4
    Last Post: 03-31-2007, 11:08 AM
  5. Confusing between Virtual and Non-virtual function
    By dv007 in forum C++ Programming
    Replies: 3
    Last Post: 01-11-2006, 06:30 PM

Tags for this Thread