Beruflich Dokumente
Kultur Dokumente
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.
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).
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).
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.
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.
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
Polymorphism in C++
ADVERTISEMENT
• Author
• Recent Articles
• Similar Articles
Author
Recent Articles
Similar Articles
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.
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.
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.
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
...
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
The actual method should be defined by the content of the object o of function
display().
Code:
class Base {
attributes:
methods:
virtual f ()
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.
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.
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)
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).
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.
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 declared with the keyword virtual.
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.
Does it ever make sense to make a function pure virtual, but still provide a body?
Problem
JG Question
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
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
};
2. Why might you declare a pure virtual function and also write a definition
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.
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.
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 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.
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.
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.