Why do we need virtual functions in C++?

2018-12-31 02:03发布

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 you can override in derived classes.

But earlier in the book, when learning about basic inheritance, I was able to override base functions in derived classes without using virtual.

So what am I missing here? I know there is more to virtual functions, and it seems to be important so I want to be clear on what it is exactly. I just can't find a straight answer online.

23条回答
闭嘴吧你
2楼-- · 2018-12-31 02:30

The virtual keyword forces the compiler to pick the method implementation defined in the object's class rather than in the pointer's class.

Shape *shape = new Triangle(); 
cout << shape->getName();

In the above example, Shape::getName will be called by default, unless the getName() is defined as virtual in the Base class Shape. This forces the compiler to look for the getName() implementation in the Triangle class rather than in the Shape class.

The virtual table is the mechanism in which the compiler keeps track of the various virtual-method implementations of the subclasses. This is also called dynamic dispatch, and there is some overhead associated with it.

Finally, why is virtual even needed in C++, why not make it the default behavior like in Java?

  1. C++ is based on the principles of "Zero Overhead" and "Pay for what you use". So it doesn't try to perform dynamic dispatch for you, unless you need it.
  2. To provide more control to the interface. By making a function non-virtual, the interface/abstract class can control the behavior in all its implementations.
查看更多
与风俱净
3楼-- · 2018-12-31 02:32

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.

查看更多
长期被迫恋爱
4楼-- · 2018-12-31 02:32

Need for Virtual Function explained [Easy to understand]

#include<iostream>

using namespace std;

class A{
public: 
        void show(){
        cout << " Hello from Class A";
    }
};

class B :public A{
public:
     void show(){
        cout << " Hello from Class B";
    }
};


int main(){

    A *a1 = new B; // Create a base class pointer and assign address of derived object.
    a1->show();

}

Output will be:

Hello from Class A.

But with virtual function:

#include<iostream>

using namespace std;

class A{
public:
    virtual void show(){
        cout << " Hello from Class A";
    }
};

class B :public A{
public:
    virtual void show(){
        cout << " Hello from Class B";
    }
};


int main(){

    A *a1 = new B;
    a1->show();

}

Output will be:

Hello from Class B.

Hence with virtual function you can achieve runtime polymorphism.

查看更多
千与千寻千般痛.
5楼-- · 2018-12-31 02:34

It helps if you know the underlying mechanisms. C++ formalizes some coding techniques used by C programmers, "classes" replaced using "overlays" - structs with common header sections would be used to handle objects of different types but with some common data or operations. Normally the base struct of the overlay (the common part) has a pointer to a function table which points to a different set of routines for each object type. C++ does the same thing but hides the mechanisms i.e. the C++ ptr->func(...) where func is virtual as C would be (*ptr->func_table[func_num])(ptr,...), where what changes between derived classes is the func_table contents. [A non-virtual method ptr->func() just translates to mangled_func(ptr,..).]

The upshot of that is that you only need to understand the base class in order to call the methods of a derived class, i.e. if a routine understands class A, you can pass it a derived class B pointer then the virtual methods called will be those of B rather than A since you go through the function table B points at.

查看更多
忆尘夕之涩
6楼-- · 2018-12-31 02:34

Virtual methods are used in interface design. For example in Windows there is an interface called IUnknown like below:

interface IUnknown {
  virtual HRESULT QueryInterface (REFIID riid, void **ppvObject) = 0;
  virtual ULONG   AddRef () = 0;
  virtual ULONG   Release () = 0;
};

These methods are left to the interface user to implement. They are essential for the creation and destruction of certain objects that must inherit IUnknown. In this case the run-time is aware of the three methods and expects them to be implemented when it calls them. So in a sense they act as a contract between the object itself and whatever uses that object.

查看更多
余生请多指教
7楼-- · 2018-12-31 02:36

I would like to add another use of Virtual function though it uses the same concept as above stated answers but I guess its worth mentioning.

VIRTUAL DESTRUCTOR

Consider this program below, without declaring Base class destructor as virtual; memory for Cat may not be cleaned up.

class Animal {
    public:
    ~Animal() {
        cout << "Deleting an Animal" << endl;
    }
};
class Cat:public Animal {
    public:
    ~Cat() {
        cout << "Deleting an Animal name Cat" << endl;
    }
};

int main() {
    Animal *a = new Cat();
    delete a;
    return 0;
}

Output:

Deleting an Animal
class Animal {
    public:
    virtual ~Animal() {
        cout << "Deleting an Animal" << endl;
    }
};
class Cat:public Animal {
    public:
    ~Cat(){
        cout << "Deleting an Animal name Cat" << endl;
    }
};

int main() {
    Animal *a = new Cat();
    delete a;
    return 0;
}

Output:

Deleting an Animal name Cat
Deleting an Animal
查看更多
登录 后发表回答