Sie sind auf Seite 1von 20

Virtual Base Classes

Because a class can be an indirect base class to a derived class more than once, C+
+ provides a way to optimize the way such base classes work. Virtual base classes
offer a way to save space and avoid ambiguities in class hierarchies that use multiple
inheritance.

Each nonvirtual object contains a copy of the data members defined in the base
class. This duplication wastes space and requires you to specify which copy of the
base class members you want whenever you access them.

When a base class is specified as a virtual base, it can act as an indirect base more
than once without duplication of its data members. A single copy of its data members
is shared by all the base classes that use it as a virtual base.

When declaring a virtual base class, the virtual keyword appears in the base lists of
the derived classes.

Consider the class hierarchy in the following figure, which illustrates a simulated
lunch line.

Simulated Lunch-Line Graph

In the figure, Queue is the base class for both CashierQueue and LunchQueue.
However, when both classes are combined to form LunchCashierQueue, the following
problem arises: the new class contains two subobjects of type Queue, one from
CashierQueue and the other from LunchQueue. The following figure shows the
conceptual memory layout (the actual memory layout might be optimized).

Simulated Lunch-Line Object

Note that there are two Queue subobjects in the LunchCashierQueue object. The
following code declares Queue to be a virtual base class:
Copy
// deriv_VirtualBaseClasses.cpp
// compile with: /LD
class Queue {};
class CashierQueue : virtual public Queue {};
class LunchQueue : virtual public Queue {};
class LunchCashierQueue : public LunchQueue, public CashierQueue {};

The virtual keyword ensures that only one copy of the subobject Queue is included
(see the following figure).

Simulated Lunch-Line Object with Virtual Base Classes

A class can have both a virtual component and a nonvirtual component of a given
type. This happens in the conditions illustrated in the following figure.

Virtual and Nonvirtual Components of the Same Class

In the figure, CashierQueue and LunchQueue use Queue as a virtual base class.
However, TakeoutQueue specifies Queue as a base class, not a virtual base class.
Therefore, LunchTakeoutCashierQueue has two subobjects of type Queue: one from
the inheritance path that includes LunchCashierQueue and one from the path that
includes TakeoutQueue. This is illustrated in the following figure.

Object Layout with Virtual and Nonvirtual Inheritance


Note
Virtual inheritance provides significant size benefits when compared with nonvirtual
inheritance. However, it can introduce extra processing overhead.

If a derived class overrides a virtual function that it inherits from a virtual base class,
and if a constructor or a destructor for the derived base class calls that function using
a pointer to the virtual base class, the compiler may introduce additional hidden
"vtordisp" fields into the classes with virtual bases. The /vd0 compiler option
suppresses the addition of the hidden vtordisp constructor/destructor displacement
member. The /vd1 compiler option, the default, enables them where they are
necessary. Turn off vtordisps only if you are sure that all class constructors and
destructors call virtual functions virtually.

The /vd compiler option affects an entire compilation module. Use the vtordisp
pragma to suppress and then reenable vtordisp fields on a class-by-class basis:

Copy
#pragma vtordisp( off )
class GetReal : virtual public { ... };
#pragma vtordisp( on )

Polymorphism in C++
By Sanskruti

On 5th February, 2007

Polymorphism in C++
ADVERTISEMENT
• Author
• Recent Articles
• Similar Articles

Author

Sanskruti ( Ambitious contributor )

Yet to provide details about himself

All articles By Sanskruti

Recent Articles

• A http-Web-server in C, Started by lionaneesh in C-C++


• Inheritance In C++ Vs Java, Started by techgeek.in in C-C++
• Exceptional Handling In C++ In Comparision With Java, Started by
techgeek.in in C-C++
• Little About STL In C++, Started by techgeek.in in C-C++
• Template In C++, Started by techgeek.in in C-C++

Similar Articles

• 100 Multiple choice questions in C, Started by coderzone in C-C++


• Huffman Encoding in C (Minimum Variance Encoding), Started by rai_gandalf
in C-C++
• ANSI C Standard, Started by Amit Ray in C-C++

Polymorphism is in short the ability to call different functions by just using one
type of function call. It is a lot useful since it can group classes and their functions
together. It is the most important part of Object-Oriented Programming.
Polymorphism is the core of object-oriented programming .C++ supports
polymorphism by allowing member functions defined in classes to be overridden with
member functions having the same names, but different implementations, in derived
classes. In selecting the appropriate member function to call in response to a function
invocation, C++ distinguishes between the static type of a reference and the
dynamic type of the object it refers to at a given point. The dynamic type must be a
descendant of the static type The invocation is type-checked based on the static type
of the reference. If the function called is a virtual member function, the member
function associated with the actual object pointed to is called dynamically at run
time. If the function is non-virtual, the call will have been statically bound to the
member function of the reference's class at compile time.

Polymorphism allows an entity (for example, variable, function or object) to take a


variety of representations. Therefore we have to distinguish different types of
polymorphism which will be outlined here.

The first type is similar to the concept of dynamic binding. Here, the type of a
variable depends on its content. Thus, its type depends on the content at a specific
time:
Code:

v := 123 /* v is integer */
... /* use v as integer */
v := 'abc' /* v "switches" to string */
... /* use v as string */
Another type of polymorphism can be defined for functions. For example, suppose
you want to define a function isNull() which returns TRUE if its argument is 0 (zero)
and FALSE otherwise.

For integer numbers this is easy:

Code:

boolean isNull(int a)
{
if (a == 0) then
return TRUE
else
return FALSE
endif
}
However, if we want to check this for real numbers, we should use another
comparison due to the precision problem:

Code:

boolean isNull(real x)
{
if (x < 0.01 and x > -0.99) then
return TRUE
else
return FALSE
endif
}
In both cases we want the function to have the name isNull. In programming
languages without polymorphism for functions we cannot declare these two functions
because the name isNull would be doubly defined. Without polymorphism for
functions, doubly defined names would be ambiguous. However, if the language
would take the parameters of the function into account it would work.

Thus, functions (or methods) are uniquely identified by:


• the name and
• the types of its parameter list.

Since the parameter list of both isNull functions differ, the compiler is able to figure
out the correct function call by using the actual types of the arguments:
Code:

var a : integer
var x : real

a = 0
x = 0.0

...

if (isNull(a)) then ... /* Use isNull(int) */


...
if (isNull(x)) then ... /* Use isNull(real) */
If a function (or method) is defined by the combination of
• its name and
• the list of types of its parameters

we can say polymorphism. This type of polymorphism allows us to reuse the same
name for functions (or methods) as long as the parameter list differs. Sometimes this
type of polymorphism is called overloading.

The last type of polymorphism allows an object to choose correct methods. Consider
the function move() again, which takes an object of class Point as its argument. We
have used this function with any object of derived classes, because the is-a relation
holds.

Now consider a function display() which should be used to display drawable objects.
The declaration of this function might look like this:

Code:

display(DrawableObject o)
{
...
o.print()
...
}
We would like to use this function with objects of classes derived from
DrawableObject:
Circle acircle
Point apoint
Rectangle arectangle

display(apoint) /* Should invoke apoint.print() */


display(acircle) /* Should invoke acircle.print() */
display(arectangle) /* Should invoke arectangle.print() */

The actual method should be defined by the content of the object o of function
display().

Since this is somewhat complicated, here is a more abstract example:

Code:
class Base {
attributes:
methods:
virtual f ()
funy()
}

class Derived inherits from Base {


attributes:
methods:
virtual funx ()
funy()
}

demo(Base o) {
o.funx ()
o.funy()
}

Base abase
Derived aderived

demo(abase)
demo(aderived)
In this example we define two classes Base and Derived. Each class defines two
methods funx() and funy(). The first method is defined as virtual. This means that if
this method is invoked its definition should be evaluated by the content of the object.

We then define a function demo() which takes a Base object as its argument.
Consequently, we can use this function with objects of class Derived as the is-a
relation holds. We call this function with a Base object and a Derived object,
respectively.

Suppose, that funx() and funy() are defined to just print out their name and the class
in which they are defined.

Then the output is as follows:


funx() of Base called.
funy() of Base called.
funx() of Derived called.
funy() of Base called.

The first call to demo() uses a Base object. Thus, the function's argument is ``filled''
with an object of class Base. When it is time to invoke method funx() it's actual
functionality is chosen based on the current content of the corresponding object o.
This time, it is a Base object. Consequently, funx() as defined in class Base is called.

The call to funy() is not subject to this content resolution. It is not marked as virtual.
Consequently, funy() is called in the scope of class Base.

The second call to demo() takes a Derived object as its argument. Thus, the
argument o is filled with a Derived object. However, o itself just represents the Base
part of the provided object aderived.
Now, the call to funx() is evaluated by examining the content of o, hence, it is called
within the scope of Derived. On the other hand, funy() is still evaluated within the
scope of Base.
dynamic binding - The property of object-oriented programming languages where the code
executed to perform a given operation is determined at run time from the class of the operand(s)
(the receiver of the message). There may be several different classes of objects which can
receive a given message. An expression may denote an object which may have more than one
possible class and that class can only be determined at run time. New classes may be created
that can receive a particular message, without changing (or recompiling) the code which sends
the message. An class may be created that can receive any set of existing messages.

C++ implements dynamic binding using "virtual member functions".

One important reason for having dynamic binding is that it provides a mechanism for selecting
between alternatives which is arguably more robust than explicit selection by conditionals or
pattern matching. When a new subclass is added, or an existing subclass changes, the
necessary modifications are localised: you don't have incomplete conditionals and broken
patterns scattered all over the program.

++ Virtual Functions
Category: C++
Comments (18)

C++ Virtual Functions

What are Virtual Functions?

Virtual, as the name implies, is something that exists in effect but not in reality. The
concept of virtual function is the same as a function, but it does not really exist although
it appears in needed places in a program. The object-oriented programming language C+
+ implements the concept of virtual function as a simple member function, like all
member functions of the class.

The functionality of virtual functions can be over-ridden in its derived classes. The
programmer must pay attention not to confuse this concept with function overloading.
Function overloading is a different concept and will be explained in later sections of this
tutorial. Virtual function is a mechanism to implement the concept of polymorphism (the
ability to give different meanings to one function).

Need for Virtual Function:


The vital reason for having a virtual function is to implement a different functionality in
the derived class.

For example: a Make function in a class Vehicle may have to make a Vehicle with red
color. A class called FourWheeler, derived or inherited from Vehicle, may have to use a
blue background and 4 tires as wheels. For this scenario, the Make function for
FourWheeler should now have a different functionality from the one at the class called
Vehicle. This concept is called Virtual Function.

Properties of Virtual Functions:

• Dynamic Binding Property:

Virtual Functions are resolved during run-time or dynamic binding. Virtual functions are
also simple member functions. The main difference between a non-virtual C++ member
function and a virtual member function is in the way they are both resolved. A non-
virtual C++ member function is resolved during compile time or static binding. Virtual
Functions are resolved during run-time or dynamic binding

• Virtual functions are member functions of a class.


• Virtual functions are declared with the keyword virtual, detailed
in an example below.
• Virtual function takes a different functionality in the derived
class.

Declaration of Virtual Function:

Virtual functions are member functions declared with the keyword virtual.

For example, the general syntax to declare a Virtual Function uses:

class classname //This denotes the base class of C++ virtual function
{
public:
virtual void memberfunctionname() //This denotes the C++ virtual
function
{
.............
............
}
};

Referring back to the Vehicle example, the declaration of Virtual function would take the
shape below:

class Vehicle //This denotes the base class of C++ virtual function
{
public:
virtual void Make() //This denotes the C++ virtual function
{
cout <<"Member function of Base Class Vehicle
Accessed"<<endl;
}
};

After the virtual function is declared, the derived class is defined. In this derived class,
the new definition of the virtual function takes place.

When the class FourWheeler is derived or inherited from Vehicle and defined by the
virtual function in the class FourWheeler, it is written as:

class Vehicle //This denotes the base class of C++ virtual function
{
public:
virtual void Make() //This denotes the C++ virtual function
{
cout <<"Member function of Base Class Vehicle
Accessed"<<endl;
}
};
class FourWheeler : public Vehicle
{
public:
void Make()
{
cout<<"Virtual Member function of Derived class
FourWheeler Accessed"<<endl;
}
};

void main()
{
Vehicle *a, *b;
a = new Vehicle();
a->Make();
b = new FourWheeler();
b->Make();
}

In the above example, it is evidenced that after declaring the member functions Make() as
virtual inside the base class Vehicle, class FourWheeler is derived from the base class
Vehicle. In this derived class, the new implementation for virtual function Make() is
placed.

The programmer might be surprised to see the function call differs and the output is then
printed as above. If the member function has not been declared as virtual, the base class
member function is always called because linking takes place during compile time and is
therefore static.

In this example, the member function is declared virtual and the address is bounded only
during run time, making it dynamic binding and thus the derived class member function
is called.
To achieve the concept of dynamic binding in C++, the compiler creates a v-table each
time a virtual function is declared. This v-table contains classes and pointers to the
functions from each of the objects of the derived class. This is used by the compiler
whenever a virtual function is needed.

This is the original GotW problem and solution substantially as posted to Usenet.
See the book More Exceptional C++ (Addison-Wesley, 2002) for the most
current solution to this GotW issue. The solutions in the book have been revised
and expanded since their initial appearance in GotW. The book versions also
incorporate corrections, new material, and conformance to the final ANSI/ISO C++
standard.

(Im)pure Virtual Functions


Difficulty: 7 / 10

Does it ever make sense to make a function pure virtual, but still provide a body?

Problem

JG Question

1. What is a pure virtual function? Give an example.

Guru Question

2. Why might you declare a pure virtual function and also write a definition
(body)? Give as many reasons or situations as you can.

Solution

1. What is a pure virtual function? Give an example.

A pure virtual function is a virtual function that you want to force derived classes
to override. If a class has any unoverridden pure virtuals, it is an "abstract class"
and you can't create objects of that type.

class AbstractClass {
public:
// declare a pure virtual function:
// this class is now abstract
virtual void f(int) = 0;
};
class StillAbstract : public AbstractClass {
// does not override f(int),
// so this class is still abstract
};

class Concrete : public StillAbstract {


public:
// finally overrides f(int),
// so this class is concrete
void f(int) { /*...*/ }
};

AbstractClass a; // error, abstract class


StillAbstract b; // error, abstract class
Concrete c; // ok, concrete class

2. Why might you declare a pure virtual function and also write a definition

Abstract Classes (C++)


Abstract classes act as expressions of general concepts from which more specific
classes can be derived. You cannot create an object of an abstract class type;
however, you can use pointers and references to abstract class types.

A class that contains at least one pure virtual function is considered an abstract
class. Classes derived from the abstract class must implement the pure virtual
function or they, too, are abstract classes.

A virtual function is declared as "pure" by using the pure-specifier syntax (described


in Class Protocol Implementation). Consider the example presented in Virtual
Functions. The intent of class Account is to provide general functionality, but objects
of type Account are too general to be useful. Therefore, Account is a good candidate
for an abstract class:

Copy
// deriv_AbstractClasses.cpp
// compile with: /LD
class Account {
public:
Account( double d ); // Constructor.
virtual double GetBalance(); // Obtain balance.
virtual void PrintBalance() = 0; // Pure virtual function.
private:
double _balance;
};

The only difference between this declaration and the previous one is that
PrintBalance is declared with the pure specifier (= 0).

Polymorphic means many forms. The object of the polymorphic class pointer can store
any derived class objects and called respective member functions when its object
member functions is called automatically instead of calling base class member
function.
By using "Virtual" key word in front of Base class member function, the base class
become polymorphic class. when this member function assigns = 0 then it becomes
pure virtual function and Base class become Abstract class.

The advantage of polymorphic class are

1. Late binding (run time)


2. It acts as interface between implementation and calling class.
3. The polymorphic class pointer can be assigned with array of derived class objects and
called respective member functions.
4. Encapsulation can be achieved in the form of API (actual implementation).

Hope this explanation helps you.


Regards,
Niranjan Ambati

Above answer was rated as good by the following members:


es1968

2.4 — Early binding and late binding


By Alex

In this chapter and the next, we are going to take a closer look at how virtual
functions are implemented. While this information is not strictly necessary to effectively
use virtual functions, it is interesting. Nevertheless, you can consider both sections
optional reading.

When a C++ program is executed, it executes sequentially, beginning at the top of


main(). When a function call is encountered, the point of execution jumps to the
beginning of the function being called. How does the CPU know to do this?

When a program is compiled, the compiler converts each statement in your C++ program
into one or more lines of machine language. Each line of machine language is given it’s
own unique sequential address. This is no different for functions — when a function is
encountered, it is converted into machine language and given the next available address.
Thus, each function ends up with a unique machine language address.

Binding refers to the process that is used to convert identifiers (such as variable and
function names) into machine language addresses. Although binding is used for both
variables and functions, in this lesson we’re going to focus on function binding.
Early binding

Most of the function calls the compiler encounters will be direct function calls. A direct
function call is a statement that directly calls a function. For example:

view source

print?
01.#include <iostream>
02.
03.void PrintValue(int nValue)
04.{
05. std::cout << nValue;
06.}
07.
08.int main()
09.{
10. PrintValue(5); // This is a direct function call
11. return 0;
12.}

Direct function calls can be resolved using a process known as early binding. Early
binding (also called static binding) means the compiler is able to directly associate the
identifier name (such as a function or variable name) with a machine address. Remember
that all functions have a unique machine address. So when the compiler encounters a
function call, it replaces the function call with a machine language instruction that tells
the CPU to jump to the address of the function.

Let’s take a look at a simple calculator program that uses early binding:

view source

print?
01.#include <iostream>
02.using namespace std;
03.
04.int Add(int nX, int nY)
05.{
06. return nX + nY;
07.}
08.
09.int Subtract(int nX, int nY)
10.{
11. return nX - nY;
12.}
13.
14.int Multiply(int nX, int nY)
15.{
16. return nX * nY;
17.}
18.
19.int main()
20.{
21. int nX;
22. cout << "Enter a number: ";
23. cin >> nX;
24.
25. int nY;
26. cout << "Enter another number: ";
27. cin >> nY;
28.
29. int nOperation;
30. do
31. {
32. cout << "Enter an operation (0=add, 1=subtract, 2=multiply):
";
33. cin >> nOperation;
34. } while (nOperation < 0 || nOperation > 2);
35.
36. int nResult = 0;
37. switch (nOperation)
38. {
39. case 0: nResult = Add(nX, nY); break;
40. case 1: nResult = Subtract(nX, nY); break;
41. case 2: nResult = Multiply(nX, nY); break;
42. }
43.
44. cout << "The answer is: " << nResult << endl;
45.
46. return 0;
47.}

Because Add(), Subtract(), and Multiply() are all direct function calls, the compiler will
use early binding to resolve the Add(), Subtract(), and Multiply() function calls. The
compiler will replace the Add() function call with an instruction that tells the CPU to
jump to the address of the Add() function. The same holds true for for Subtract() and
Multiply().

Late Binding

In some programs, it is not possible to know which function will be called until runtime
(when the program is run). This is known as late binding (or dynamic binding). In C++,
one way to get late binding is to use function pointers. To review function pointers
briefly, a function pointer is a type of pointer that points to a function instead of a
variable. The function that a function pointer points to can be called by using the function
call operator (()) on the pointer.

For example, the following code calls the Add() function:

view source

print?
01.int Add(int nX, int nY)
02.{
03. return nX + nY;
04.}
05.
06.int main()
07.{
08. // Create a function pointer and make it point to the Add
function
09. int (*pFcn)(int, int) = Add;
10. cout << pFcn(5, 3) << endl; // add 5 + 3
11.
12. return 0;
13.}

Calling a function via a function pointer is also known as an indirect function call. The
following calculator program is functionally identical to the calculator example above,
except it uses a function pointer instead of a direct function call:

view source

print?
01.#include <iostream>
02.using namespace std;
03.
04.int Add(int nX, int nY)
05.{
06. return nX + nY;
07.}
08.
09.int Subtract(int nX, int nY)
10.{
11. return nX - nY;
12.}
13.
14.int Multiply(int nX, int nY)
15.{
16. return nX * nY;
17.}
18.
19.int main()
20.{
21. int nX;
22. cout << "Enter a number: ";
23. cin >> nX;
24.
25. int nY;
26. cout << "Enter another number: ";
27. cin >> nY;
28.
29. int nOperation;
30. do
31. {
32. cout << "Enter an operation (0=add, 1=subtract, 2=multiply):
";
33. cin >> nOperation;
34. } while (nOperation < 0 || nOperation > 2);
35.
36. // Create a function pointer named pFcn (yes, the syntax is
ugly)
37. int (*pFcn)(int, int);
38.
39. // Set pFcn to point to the function the user chose
40. switch (nOperation)
41. {
42. case 0: pFcn = Add; break;
43. case 1: pFcn = Subtract; break;
44. case 2: pFcn = Multiply; break;
45. }
46.
47. // Call the function that pFcn is pointing to with nX and nY as
parameters
48. cout << "The answer is: " << pFcn(nX, nY) << endl;
49.
50. return 0;
51.}

In this example, instead of calling the Add(), Subtract(), or Multiply() function directly,
we’ve instead set pFcn to point at the function we wish to call. Then we call the function
through the pointer. The compiler is unable to use early binding to resolve the function
call pFcn(nX, nY) because it can not tell which function pFcn will be pointing to at
compile time!

Late binding is slightly less efficient since it involves an extra level of indirection. With
early binding, the compiler can tell the CPU to jump directly to the function’s address.
With late binding, the program has to read the address held in the pointer and then jump
to that address. This involves one extra step, making it slightly slower. However, the
advantage of late binding is that it is more flexible than early binding, because decisions
about what function to call do not need to be made until run time.

In the next lesson, we’ll take a look at how late binding is used to implement virtual
functions.

Re: early binding in a C++


Using early binding direct function calls can be resolved. In early binding compiler is
directly is associate with the identifier name using machine address. All function
have same machine address so when the compiler calls to a function, it replace the
call with machine language instruction that tells CPU to jump to the address of the
function.

Code:
void Print(int n)
{
std::cout << n;
}

int main()
{
Print (6);
return 0;
}
Advantage of early binding is as follows-
1 Your code is run faster than late binding because it complied all things first.
2 Debugging is more faster and compiler is able to spot syntax error.
3 You have full access over a project so that you can type keyword and dot operator
to get list of properties and methods.
4 You have full access to the application object via the Object browser.

C++ General: What is the 'this' pointer?


Q: What is the 'this' pointer?

A: It is a misbelief that the 'this' pointer is a hidden member of a class or struct. It


is a hidden parameter of non-static member functions. When you declare a function
the compiler adds an extra parameter to function's prototype. The type of the
parameter depends on how the function is declared. According to C++ standard,
9.3.2.1:
Quote:
In the body of a nonstatic member function, the keyword this is a non-lvalue
expression whose value is the address of the object for which the function is called.
The type of this in a member function of a class X is X*. If the member function is
declared const, the type of this is const X*, if the member function is declared
volatile, the type of this is volatile X*, and if the member function is declared const
volatile, the type of this is const volatile X*.
For instance
Code:
class T
{
public:
void foo(int a);
int goo() const;
};
is actually:
Code:
class T
{
public:
void foo(T* this , int a);
int goo(const T* this) const;
};
Static member functions, which don’t have class scope, do not have this extra
parameter. One consequence is that you cannot use a non-static member function as
a thread function even if it has the correct prototype
Code:
UINT ThreadFunction(LPVOID param);
because that in fact the prototype (when non-static) is
Code:
UINT ThreadFunction(T* this, LPVOID param);

Das könnte Ihnen auch gefallen