?
Solved

Why pure virtual method use non pure virtual hidden pointer ?

Posted on 2003-02-20
9
Medium Priority
?
415 Views
Last Modified: 2013-11-15
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
Comment
Question by:loiclc
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
9 Comments
 
LVL 12

Expert Comment

by:Salte
ID: 7987391
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
 
LVL 5

Expert Comment

by:Kimpan
ID: 7988936
Alf, you are one hell of a text spitter. I am impressed of your C++ knowledge as well
0
 

Author Comment

by:loiclc
ID: 7992340
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
Enterprise Mobility and BYOD For Dummies

Like “For Dummies” books, you can read this in whatever order you choose and learn about mobility and BYOD; and how to put a competitive mobile infrastructure in place. Developed for SMBs and large enterprises alike, you will find helpful use cases, planning, and implementation.

 
LVL 12

Expert Comment

by:Salte
ID: 7995954
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
 

Author Comment

by:loiclc
ID: 8007002
I was talking about interface in term of real OOP language where interface is only an API without implementation.

Loic
0
 
LVL 12

Accepted Solution

by:
Salte earned 300 total points
ID: 8007313
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
 
LVL 2

Expert Comment

by:udil
ID: 8868885
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
 
LVL 12

Expert Comment

by:Salte
ID: 8869746
I believe my response here should be accepted.

Alf
0
 
LVL 40

Expert Comment

by:Kyle Abrahams
ID: 9355956
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

Industry Leaders: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

This article is an update and follow-up of my previous article:   Storage 101: common concepts in the IT enterprise storage This time, I expand on more frequently used storage concepts.
Are you looking to recover an email message or a contact you just deleted mistakenly? Or you are searching for a contact that you erased from your MS Outlook ‘Contacts’ folder and now realized that it was important.
The goal of the video will be to teach the user the concept of local variables and scope. An example of a locally defined variable will be given as well as an explanation of what scope is in C++. The local variable and concept of scope will be relat…
The viewer will learn additional member functions of the vector class. Specifically, the capacity and swap member functions will be introduced.
Suggested Courses

801 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question