vtable cpp dynamic linking
Why can’t we create object of abstract class?
we cant create object of abstract class becoz, in the vtable the vtable entry for the abstract class functions will be NULL, for functions that are defined as pure virtual functions…
even if there is a single pure virtual function in the class the class becomes as abstract class.
If there is a virtual function in your class the compiler automatically creates a table called virtual function table .. to store the virtual function addresses….
if the function is a pure virtual function the vtable entry for that function will be NULL.Even if there is a single NULL entry in the function table the compiler does not allow to create the object.
Space overhead associated with vtable:
Using virtual functions has the following costs in memory and execution speed: Each object has its size increased by the amount needed to hold an address. For each class, the compiler creates a table of addresses of virtual functions. For each function call, there is an extra step of looking up the address on the table.
So , in short
- one pointer is created every instance of the class.(pointer pointing to vtable)
- There is an additional per-class overhead of a vtable.
Each time you create a class that contains virtual functions, or you derive from a class that contains virtual functions, the compiler creates a unique VTABLE for that class.
If you don’t override a function that was declared virtual in the base class, the compiler uses the address of the base-class version in the derived class.Dynamic binding occurs in case of virtual functions.
In the simple case of single inheritance, a child class starts with a copy of the parent class’s vtable and then gets an overridden entry for each virtual method in the child class that overrides a parent class’s method. ( and it also gets a new entry for every virtual function in the child class that does not override a parent class method )
Example:
class ABase{
int iMem;
};
class BBase : public virtual ABase {
int iMem;
};
class CBase : public virtual ABase {
int iMem;
};
class ABCDerived : public BBase, public CBase {
int iMem;
};
And if you check the size of these classes, it will be:
Size of ABase : 4
Size of BBase : 12
Size of CBase : 12
Size of ABCDerived : 24
Because BBase and CBase are derived from ABase virtually, they will also have an virtual base pointer. So, 4 bytes will be added to the size of the class (BBase and CBase). That is sizeof ABase + size of int + sizeof Virtual Base pointer.
Size of ABCDerived will be 24 (not 28 = sizeof (BBase(12) + CBase(8) + int member(4))) because it will maintain only one Virtual Base pointer (Same way of maintaining virtual table pointer).
Example 2:
using namespace std;
class CPolygon {
protected: int width, height;
public: void set_values (int a, int b) { width=a; height=b; }
virtual int area () { return (0); }
};
class CRectangle: public CPolygon {
public: int area () {
return (width * height); }
};
class CTriangle: public CPolygon {
public: int area () { return (width * height / 2); }
};
int main () {
CRectangle rect; CTriangle trgl; CPolygon poly;
CPolygon * ppoly1 = ▭
CPolygon * ppoly2 = &trgl;
CPolygon * ppoly3 = &poly;
ppoly1->set_values (4,5);
ppoly2->set_values (4,5);
ppoly3->set_values (4,5);
cout << ppoly1->area() << endl; cout << ppoly2->area() << endl; cout << ppoly3->area() << endl; return 0;
}
SInce the three classes (CPolygon, CRectangle and CTriangle) have all the same members: width, height, set_values() and area(). The member function area() has been declared as virtual in the base class because it is later redefined in each derived class. If you remove this virtual keyword from the declaration of area() within CPolygon, and then you run the program the result will be 0 for the three polygons instead of 20, 10 and 0. That is because instead of calling the corresponding area() function for each object (CRectangle::area(), CTriangle::area() and CPolygon::area(), respectively), CPolygon::area() will be called in all cases since the calls are via a pointer whose type is CPolygon*.
Therefore, what the virtual keyword does is to allow a member of a derived class with the same name as one in the base class to be appropriately called from a pointer, and more precisely when the type of the pointer is a pointer to the base class but is pointing to an object of the derived class, As in the above example. A class that declares or inherits a virtual function is called a polymorphic class. Note that despite of its virtuality, we have also been able to declare an object of type CPolygon and to call its own area() function, which always returns 0.
The purpose of declaring a pure virtual function is to have derived classes inherit a function interface only. On the other hands, the purpose of declaring a simple virtual function is to have derived classes inherit a function interface as well as a default implementation. The purpose of declaring a non-virtual function is to have derived classes inherit a function interface as well as a mandatory implementation.Derived classes do not have to implement all virtual functions themselves. They only need to implement the pure ones.
The purpose of virtual functions is to allow customization of derived class implementations.Dynamic programming is based on the kind of object on which it is called. With virtual keyword is used , the method called is of the class object.The pointer (or reference) type is known at compile time while object type might be determined at runtime.
The reason virtual function cannot be inline are that a vtable is created for every class that have the address of all member function. Since inline function do not have a address, virtual functions have to be treated non inline.
Memory Layout in Virtual Inheritance
In order to keep track of the single instance of the PoweredDevice object, the compiler will provide a virtual function table (vtable) for classes Scanner and Printer. When a Copier object is constructed, it creates one PoweredDevice instance, a Scanner instance and a Printer instance.
The Scanner and Printer classes have a virtual pointer in their vtables that stores the offset to the PoweredDevice class. When the Scanner class or the Printer class goes to access any fields of the PoweredDevice , it uses the virtual pointer in its vtable to find the PoweredDevice object and find the field in it.
diamond / dreaded rectangle
class PoweredDevice { public: PoweredDevice(int nPower) { cout << “PoweredDevice: “ << nPower << endl; } }; class Scanner: public PoweredDevice { public: Scanner(int nScanner, int nPower) : PoweredDevice(nPower) { cout << “Scanner: “ << nScanner << endl; } }; class Printer: public PoweredDevice { public: Printer(int nPrinter, int nPower) : PoweredDevice(nPower) { cout << “Printer: “ << nPrinter << endl; } }; class Copier: public Scanner, public Printer { public: Copier(int nScanner, int nPrinter, int nPower) : Scanner(nScanner, nPower), Printer(nPrinter, nPower) { } }; |
If you were to create a Copier class object, by default you would end up with two copies of the PoweredDevice class — one from Printer, and one from Scanner. This has the following structure:
We can create a short example that will show this in action:
1 2 3 4 | int main() { Copier cCopier(1, 2, 3); } |
This produces the result:
PoweredDevice: 3 Scanner: 1 PoweredDevice: 3 Printer: 2
As you can see, PoweredDevice got constructed twice.
While this is sometimes what you want, other times you may want only one copy of PoweredDevice to be shared by both Scanner and Printer. To share a base class, simply insert the “virtual” keyword in the inheritance list of the derived class. This creates what is called a virtual base class, which means there is only one base object that is shared. Here is the an example showing how to use to virtual keyword to create a shared base class:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | class PoweredDevice { }; class Scanner: virtual public PoweredDevice { }; class Printer: virtual public PoweredDevice { }; class Copier: public Scanner, public Printer { }; |
Now, when you create a Copier class, you will get only one copy of PoweredDevice that will be shared by both Scanner and Printer.
However, this leads to one more problem: if Scanner and Printer share a PoweredDevice base class, who is responsible for creating it?
The answer, as it turns out, is Copier. The Copier constructor is responsible for creating PoweredDevice. Consequently, this is one time when Copier is allowed to call a non-immediate-parent constructor directly:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | class PoweredDevice { public: PoweredDevice(int nPower) { cout << “PoweredDevice: “ << nPower << endl; } }; class Scanner: virtual public PoweredDevice { public: Scanner(int nScanner, int nPower) : PoweredDevice(nPower) { cout << “Scanner: “ << nScanner << endl; } }; class Printer: virtual public PoweredDevice { public: Printer(int nPrinter, int nPower) : PoweredDevice(nPower) { cout << “Printer: “ << nPrinter << endl; } }; class Copier: public Scanner, public Printer { public: Copier(int nScanner, int nPrinter, int nPower) : Scanner(nScanner, nPower), Printer(nPrinter, nPower), PoweredDevice(nPower) { } }; |
This time, our previous example:
1 2 3 4 | int main() { Copier cCopier(1, 2, 3); } |
produces the result:
PoweredDevice: 3 Scanner: 1 Printer: 2
As you can see, PoweredDevice only gets constructed once.
There are a few details that we would be remiss if we did not mention.
First, virtual base classes are created before non-virtual base classes, which ensures all bases get created before their derived classes.
Second, note that the Scanner and Printer constructors still have calls to the PoweredDevice constructor. If we are creating an instance of Copier, these constructor calls are simply ignored because Copier is responsible for creating the PoweredDevice, not Scanner or Printer. However, if we were to create an instance of Scanner or Printer, the virtual keyword is ignored, those constructor calls would be used, and normal inheritance rules apply.
Third, if a class inherits one or more classes that have virtual parents, the most derived class is responsible for constructing the virtual base class. In this case, Copier inherits Printer and Scanner, both of which have a PoweredDevice virtual base class. Copier, the most derived class, is responsible for creation of PoweredDevice. Note that this is true even in a single inheritance case: if Copier was singly inherited from Printer, and Printer was virtually inherited from PoweredDevice, Copier is still responsible for creating PoweredDevice.
Delegating to a sister class
A powerful technique that arises from using virtual inheritance is to delegate a method from a class in another class by using a common abstract base class. This is also called cross delegation. Let’s assume we have a similar scenario like in the diamond example, with small changes. Suppose thewrite() method in transmitter class needs to access the read() method from receiver for the radio to work (this is kind of a weird behavior, but let’s take it for the sake of illustration) :
class storable { public: storable(const char*); virtual void read()=0; <span style="color: #339966;">//this becomes pure virtual making storable an abstract</span> virtual void write(); <span style="color: #339966;">//class</span> virtual ~storable(); private: .... } class transmitter: public virtual storable { public: void write() { read(); .... } } class receiver: public virtual storable { public: void read(); } class radio: public transmitter, public receiver { public: ... } int main() { radio *rad = new radio(); receiver *r1 = rad; transmitter *r2 =rad; rad->write(); r1->write(); r2->write(); return 1; }
Because of virtual inheritance, when the write() function from the transmitter class is called, the method read() from the receiver class gets called (since the transmitter class doesn’t have a read() function). In the above hierarchy we can instantiate only the radio class because transmitter and receiver are abstract due to virtual inheritance.