Understanding virtual destructors

I tried to familiarize myself with OOP concepts but could not understand the concept virtual

.

  • Can be created virtual destructor

    , but not virtual constructor

    . Why?
  • How is it virtual destructors

    processed internally? I mean the link Virtual destructors illustrates the concept, but my question is how vptr

    are vtable

    (Derivatives and Bases) called? (In the case of virtual member functions, when such a scenario occurs at all, the function that vptr

    the Derived class indicates is that it is being called)
  • Are there any other scenarios that you might need to use virtual destructor

    ?

Can anyone help me understand the above concepts with links / examples?

+3


source to share


6 answers


First, a little about the difference between virtual functions and non-virtual functions:

Every non-virtual function call you have in your code can be resolved at compile time or linkage.

Solved, we realize that the address of a function can be computed by the compiler or linker.

Thus, in the generated object code, the function call can be replaced with an op-code to jump to the address of this function in memory.

With virtual functions, you can call functions that can only be resolved at runtime.

Instead of explaining this, skip the simple scenario:

class Animal
{
    virtual void Eat(int amount) = 0;
};

class Lion : public Animal
{
    virtual void Eat(int amount) { ... }
};

class Tiger : public Animal
{
    virtual void Eat(int amount) { ... }
};

class Tigon : public Animal
{
    virtual void Eat(int amount) { ... }
};

class Liger : public Animal
{
    virtual void Eat(int amount) { ... }
};

void Safari(Animal* animals[], int numOfAnimals, int amount)
{
    for (int i=0; i<numOfAnimals; i++)
        animals[i]->Eat(amount);
    // A different function may execute at each iteration
}

      

As you can probably imagine, the function Safari

allows you to be flexible and feed different animals.

But since the exact type of each animal is unknown until the time of execution, that is, the exact function Eat

that needs to be called.


A class constructor cannot be virtual because:

The call to a virtual function of an object is performed through the V-table of the object class.

Each object contains a pointer to the V-table of its class, but this pointer is only initialized at runtime when the object is created.

In other words, this pointer is only initialized when the constructor is called, so the constructor itself cannot be virtual.



Also, it doesn't make sense for the constructor to be virtual in the first place.

The idea behind virtual functions is that you can call them without knowing the exact type of object they are being called with.

When you create an object (i.e. when you implicitly call a constructor), you know exactly what type of object you are creating, so you don't need this mechanism.


The base class destructor must be virtual because:

When you statically allocate an object whose class inherits from the base class, then at the end of the function (if the object is local) or in the program (if the object is global) the class destructor is automatically called and, in turn, calls the destructor of the base class.

In this case, it makes no sense for the destructor to be virtual.

On the other hand, when you dynamically allocate ( new

) an object whose class inherits from the base class, then you need to dynamically free ( delete

) it at some later point in program execution.

The operator delete

accepts a pointer to an object, where the type of the pointer can be the base class.

In this case, if the destructor is virtual, then the operator delete

calls the destructor of the class, which in turn calls the destructor of the base class.

But if the destructor is not virtual, then the operator delete

calls the destructor of the base class, and the destructor of the actual class is never called.

Consider the following example:

class A
{
    A() {...}
    ~A() {...}
};

class B: public A
{
    B() {...}
    ~B() {...}
};

void func()
{
    A* b = new B(); // must invoke the destructor of class 'B' at some later point
    ...
    delete b; // the destructor of class 'B' is never invoked
}

      

+6


source


You can create a virtual destructor, but not a virtual constructor. Why?

Virtual functions are dispatched according to the type of object they are calling. When the constructor is called, there is no object - it's up to the constructor to create it. Without an object, there is no virtual dispatch capability, so a constructor cannot be virtual.

How are virtual destructors handled internally?

The internal details of virtual dispatch are implementation-defined; language does not specify implementation, only behavior. Typically, a destructor is called using a virtual table, just like any virtual function.



how is the vptr of both vtables (Derived and Base) called?

In fact, only the most derived destructor will be called. All destructors, virtual or not, will implicitly call the destructors of all subobjects of the element and the direct base class. (The situation is a little more complicated with virtual inheritance, but that is beyond the scope of this question.)

Are there other scenarios where a virtual destructor might be needed?

You need to support polymorphic deletion; that is, be able to delete an object of a derived type using a pointer to the base type. Without virtual destructor for base type which is not allowed and will give undefined behavior.

+3


source


  • because at runtime the virtual function is called, however at the initialization stage the constructors are called, the object is not constructed. Therefore it makes no sense to have a virtual constructor.

  • and. the reason why only the base class descriptor is called in your reference, the destructor is not marked as virtual, so the address of the descriptor is associated with the Base class destructor at compile / link time, and obviously the pointer type is Base instead of Derived at compile time.

    b. why both constructors Base and Derived are called after adding the virtual base descriptor. This is the same behavior as below: Derived d; // when d exits the lifecycle, both Derived and Base desctructor are called.

  • Suppose if you have at least one virtual function you must have a virtual descriptor.

+2


source


You can create a virtual destructor, but not a virtual constructor. Why?

I will try to explain this in a non-professional setting. A class in C ++ only exists after the constructor completes. Each base class exists before the derived class and its members (including vtable links) are initialized. Hence, having a virtual constructor does not make sense (since you need to know the type to build it). Also (in C ++), calling virtual functions from the constructor does not work (since the vtable part of the derived class has not been configured). If you think about it carefully, allowing virtual functions to be called from the counter constructor opens up a can of worms (for example, if the virtual functions of derived classes are called before the member is initialized).

As far as destructors are concerned, at the point of destruction the vtable is "intact" and we (the C ++ runtime) are fully type aware (so to speak). The destructor of the most derived part of the type is found (if virtual, via vtable) and, therefore, the destructor, and, of course, all bases can be called.

How are virtual destructors handled internally? I mean the link Virtual destructors illustrate the concept, but my question is, how is the vptr of both vtables (Derived and Base) called?

Destructors are handled in the same way as regular virtual functions (that is, addresses are looked up in the vtable if they are virtual at the expense of one (perhaps 2?) Additional level / s of indirection). In addition, C ++ guarantees that all base destructors must execute (in the opposite order of the construct, which relies on the order of declaration) after the derived destructor completes.

It is possible to simulate / simulate a virtual structure using templates such as a prototype template (or cloning) or using factories. In such cases, there is either an instance of a real type (which is used polymorphically) or there is a factory (coming from an abstract factory) that creates a type (via a virtual function) based on the knowledge provided.

Hope it helps.

+2


source


I assume we have a base class A and it got B.

1: You can delete B using pointer A and then the correct method is to call B's destructor too. However, you simply cannot say that object B should be instantiated, as long as you are actually just calling the constructor of A. There is simply no such case. You can say:

A* a = new B ();

      

or

B b;

      

But both of them call B's constructor directly.

2: Well, I'm not entirely sure, but I think it will go through the appropriate part of the class hierarchy and look for the closest function call. If the function is not virtual, it stops the iteration and calls it.

3: You should always use a virtual destructor if you want to inherit from this class. If it's a final class, you shouldn't.

0


source


I spent a couple of days trying to figure out why my derivative virtual destructors were not being called before opening the answer, so hopefully I can save a lot more grief with this answer.

I started using derived classes three and four levels deep in my project. The virtual functions seemed to work fine, but then I found that I had a massive memory leak because my destructors were not being called. No compiler or runtime errors - the destructors were simply not called.

There is a ton of documentation and examples on the internet about this, but none of them were helpful because my syntax was correct.

I figured that if the compiler wasn't going to call my destructors, I needed to create my own virtual destruction method to call. Then I got a compiler error that solved the problem - "class if Forward Reference". Adding include for the derived class header files in the base class solved the problem. The compiler needs a class definition to call the destructor!

I suggest including the header file in the base and intermediate classes when creating a new derived class. It's probably also a good idea to add conditional debug code to your destructors to check that they are called bing.

Bob rice

0


source







All Articles