Want to protect your cyber security and still get fast solutions? Ask a secure question today.Go Premium

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 419
  • Last Modified:

Why pure virtual method use non pure virtual hidden pointer ?

Take this code (under VC6 or GCC3):

#include <stdio.h>

class EmptyBase
{
public:
    virtual void f() = 0;
};

class Zero: public EmptyBase
{
public:
    void f()
    {
        //...
    };
};

int main(int argc, char** argv)
{
    printf("sizeof(EmptyBase) = %d \n", sizeof(EmptyBase));
    printf("sizeof(Zero) = %d \n", sizeof(Zero));
}

And you get:
sizeof(EmptyBase) = 4
sizeof(Zero) = 4

I don't understand why the result is nont:
sizeof(EmptyBase) = 1
sizeof(Zero) = 1

Indeed, if EmptyBase::f() was defined only virtual as:
class EmptyBase
{
public:
    virtual void f()
    {
        // ...
    };
};
I will be agree, because EmptyBase has a virtual method and so there is hidden pointer to virtual method table.

But here EmptyBase::f() is PURE virtual method, so there is no dynamic choice to do, only static. So why there is hidden pointer ?
0
loiclc
Asked:
loiclc
1 Solution
 
SalteCommented:
Defining a pure virtual function will also create a virtual method table.

Consider:

class B {
public:
   virtual void f() = 0;
};

class A : public B {
public:
    virtual void f() {};
};

B * p = new A;

p -> f();

How is the function reached? It is reached through the virtual table of the A object but how does the compiler know that the object pointed to by p at all HAS a virtual function table pointer? The pointer is of type B and not A. Clearly it knows that because every object pointed to by a B pointer MUST have a virtual function table. In other words, the class B itself must also have a virtual function table.

In this simple example that function table will only contain one slot for f() and it will be a null pointer since f() is pure virtual. But this is a very specific example, in the general case you have a mix of some non pure virtual functions and some pure virtual functions and the the class B will have a virtual function table associated with it. The table is even used:

When you construct an A object you often write something like this:

A::A(...) : B(...) { ...body of A.. }

The reason is that when you call the constructor for A that constructor call will contain a call to A's base B's constructor before the body of the constructor is executed. IN other words, the body of A::A is almost as if you had written:

void A::A()
{
   B::B(); // call B's constructor.
   ....execute the statements in A's constructor....
}

I wrote it like a regular function here with void return type to indicate that this is like what the function body of the constructor look like.

When calling that B's constructor, you don't have any A object yet and so it will make a B object. To do that the B constructor will initialize the object by setting the virtual function table pointer to B's virtual function table. If you call any virtual functions inside the constructor the calls will go to virtual functions defined in B. Since these might be (and in our case is) pure virtual you will get a run time error if you call any pure virtual functions from B's constructor.

Once B's constructor is done, it will return to A's constructor but before the body of the constructor is executed A's constructor will overwrite the virtual function table pointer with the pointer value pointing to A's virtual function table.

So, this is how it goes:

1. You call A's constructor.
2. A's constructor calls B's constructor.
3. B's constructor set vtableptr to 'vtable for B'.
4. B's constructor's body is executed.
5. B's constructor return to A's constructor.
6. A's constructor set vtableptr to 'vtable for A'.
7. A's constructor's body is executed.
8. A's constructor return to your code.

A similar thing happen in reverse when you call destructors. Before B's destructor ~B() is executed, the vtable pointer is set to point to B's vtable. Again, any virtual functions cannot go to A's virtual table since the A object doesn't exist any more. When B's destructor is called the A part of the object is already gone.

So not only do B has a virtual function table, it's also used - even if B has pure virtual functions and cannot be instantiated.

So, EVERY B object has room for a virtual function table pointer. Including any object derived from B. If B has pure virtual functions a B object by itself cannot exist but all the derived classes will inherit that virtual function table pointer.

So, sizeof(B) == sizeof(A) == sizeof(void *) in the above case.

Note that this means that if you have a baseclass that has virtual table pointer, you can have lots of virtual functions and they won't increase the size of the derived objects one bit. They will only increase the size of the object's virtual table, but that is a static object (only one copy per class).

Alf
0
 
KimpanCommented:
Alf, you are one hell of a text spitter. I am impressed of your C++ knowledge as well
0
 
loiclcAuthor Commented:
Thank Alf, I'have indeed made a mistake by forgoting pointer reasonment.

But, my error is most that I have surestimate compiler optimization. In our case, compiler can very well reached f() without virtual table (I know the principe of virtual table).

Ok it's a very specialized case, but I'm surprising that compiler don't opitmize this special case. Indeed it is the only case to implement interface correctly, ie only with pure virtual method.

Loic
0
Concerto Cloud for Software Providers & ISVs

Can Concerto Cloud Services help you focus on evolving your application offerings, while delivering the best cloud experience to your customers? From DevOps to revenue models and customer support, the answer is yes!

Learn how Concerto can help you.

 
SalteCommented:
Not really,

An interface can also have non-pure virtual functions.

A virtual function can have implementation that provides a default behavior. Since it is an interface that default behavior is usually defined in terms of other virtual functions and those will typically be ultimately pure virtual functions.

The interface can even include non-virtual functions that are defined in terms of the virtual functions of the interface.

A subclass implementing the interface can then implement all the pure virtual functions and may choose to override some or all of the non-pure ones if their defaults isn't sufficient.

The non-virtual functions cannot be amended though and the interface really locks them up. However, as they are defined in terms of the virtual functions they usually implement inherent properties of those functions.

For example an interface giving a function:

virtual bool is_property_true() const = 0;

and then add a non-virtual function:
bool is_property_false() const
{ return ! is_property_true(); }

This isn't even a virtual function but it is inherently such that if the property is false it cannot be true and vice versa and so the above implementation is sufficient as a non-virtual function.

In other cases you will make the function virtual but provide a reasonable default that works in 90% of the cases and then those classes that cannot use that default will write their own while those those classes that can use that default can implement the interface without making those functions.

An interface does not have to have only pure virtual functions. It must have at least one pure virtual function and it has one pure virtual function for each property or method that is independent of each other but properties or methods that can be defined in terms of other properties or methods doesn't have to be - and shouldn't be - pure virtual.

Alf
0
 
loiclcAuthor Commented:
I was talking about interface in term of real OOP language where interface is only an API without implementation.

Loic
0
 
SalteCommented:
C++ doesn't have an interface construct. It is usually implemented by using class and specifically a class with pure virtual functions.

However, the language itself doesn't know what "interface" is.

Also, I think you won't get far by using the term "real OOP" language.

"real OOP" language and interface are relatively independent thingies. There's nothing in the definition of object orientation that requires the presence of interfaces. I can agree that interfaces is a good construct, it is one of the plusses of languages like Java and C# that they have such construct. But it doesn't mean that languages which lack them isn't object oriented. Especially not since C++ does a good job in emulating them by using classes with pure virtual functions.

Perhaps you should try to learn "what IS object orientation" before you try to make such statements?

Alf
0
 
udilCommented:
This question has been abandoned. I will make a recommendation to the moderators on its resolution in a week or two. I appreciate any comments that would help me to make a recommendation.

In the absence of responses, I may recommend DELETE unless it is clear to me that it has value as a PAQ. Silence = you don't care.

PLEASE DO NOT ACCEPT THIS COMMENT AS AN ANSWER!

Udil
EE Cleanup Volunteer
0
 
SalteCommented:
I believe my response here should be accepted.

Alf
0
 
Kyle AbrahamsSenior .Net DeveloperCommented:
No comment has been added lately, so it's time to clean up this TA.
I will leave a recommendation in the Cleanup topic area that this question is:

Grade of A to Salte.

Please leave any comments here within the next seven days.
 
PLEASE DO NOT ACCEPT THIS COMMENT AS AN ANSWER!

Ged
EE Cleanup Volunteer

0

Featured Post

Free recovery tool for Microsoft Active Directory

Veeam Explorer for Microsoft Active Directory provides fast and reliable object-level recovery for Active Directory from a single-pass, agentless backup or storage snapshot — without the need to restore an entire virtual machine or use third-party tools.

Tackle projects and never again get stuck behind a technical roadblock.
Join Now