• Keine Ergebnisse gefunden

Run-time type identification

Im Dokument or an (Seite 124-129)

The typeid operator

To use the typeid operator you must include the typeinfo.h header file.

The recent addition of run-time type identification (RTTI) into the ANSI/

ISO C++ working paper makes it possible to write portable code that can determine the actual type of a data object at run time even when the code has access only to a pointer or reference to that object. This makes it possible, for example, to convert a pointer to a virtual base class into a pointer to the derived type of the actual object. See page 110 for a description of the dynamic_cast operator, which uses run-time type information.

The RTTI mechanism also lets you check whether an object is of some particular type and whether two objects are of the same type. You can do this with typeid operator, which determines the actual type of its argument and returns a reference to an object of type const Type_info, which describes that type. You can also use a type name as the argument to typeid, and typeid will return a reference to a const Type_info object for that type. The class Type_info provides an operator== and an operator!= that you can use to determine whether two objects are of the same type. Class Type_info also provides a member function name that returns a pointer to a char array that holds the name of the type. See the Library Reference, Chapter 10, for a description of class Type_info.

You can use typeid to get run-time information about types or expressions.

A call to typeid returns a reference to an object of type constType_info. The returned object represents the type of the typeid operand.

If the typeid operand is a dereferenced pointer or a reference to a poly-morphic type, typeid returns the dynamic type of the actual object pointed

Example

Program output

The __ rtti keyword and the -RToption

114

or referred to. If the operand is non-polymorphic, typeid returns an object that represents the static type.

You can use the typeid operator with fundamental data types as well as user-defined types.

II HOW TO USE typeid, Type_info::before() , and Type_info::name().

#include <iostream.h>

#include <string.h>

#include <typeinfo.h>

class A { };

class B : A { };

char *true = "true";

char *false = "false";

void main() char C;

float X;

if (typeid( C ) == typeid( X ))

cout « "C and X are the same type." « endl;

else cout « "C and X are NOT the same type." « endl;

cout « typeid(int) .name() i

cout « " before" « typeid(double) .name() « ": " «

(typeid(int) .before(typeid(double)) ? true: false) « endl;

cout « typeid(double) .name();

cout « " before" « typeid(int) .name() « ": " «

(typeid(double) .before(typeid(int)) ? true: false) « endl;

cout « typeid(A) .name()i

cout « " before" « typeid(B) .name() « ": " «

(typeid(AJ .before(typeid(B)) ? true: false) « endl;

C and X are ,NOT the same type.

int before double: false double before int: true A before B: true

If the typeid operand is a dereferenced NULL pointer, the Bad _typeid exception is thrown. See the Library Reference, Chapter 10, for a description of Bad _typeid.

RTTI is enabled by default in Borland C++. You can use the -RT

command-line option to disable it ( -RT- ) or to enable it (-RT). If RTTI is disabled, or if the argument to typeid is a pointer or a reference to a non-polymorphic class (see page 155 for a discussion of non-polymorphic classes), typeid returns a reference to a const Type_info object that describes the

Borland C++ Programmers Guide

Example

declared type of the pointer or reference, and not the actual object that the pointer or reference is bound to.

In addition, even when RTTI is disabled, you can force all instances of a particular class and all classes derived from that class to provide polymor-phic run-time type identification (where appropriate) by using the Borland C++ keyword __ rtti in the class definition.

When you use the -RT -compiler option, if any base class is declared __ rtti, then all polymorphic base classes must also be declared __ rtti.

struct __ rtti 81 { virtual slfunc() i}i II Polymorphic struct __ rtti 82 { virtual s2func() i}i II Polymorphic struct X : 81, 82 { } i

If you turn off the RTTI mechanism (by using the -RT-compiler option), RTTI might not be available for derived classes. When a class is derived from multiple classes, the order and type of base classes determines whether or not the class inherits the RTTI capability.

When you have polymorphic and non-polymorphic classes, the order of inheritance is important. If you compile the following declarations with -RT-, you should declare X with the __ rtti modifier. Otherwise, switching the order of the base classes for the class X results in the compile-time error Can't inherit non-RTTI class from RTTI base 'Sl'.

Note that the class X is explicitly declared with __ rtti. This makes it safe to mix the order and type of classes.

struct __ rtti 81 { virtual func() i}i II Polymorphic class

struct 82 { }i II Non-polymorphic class

struct __ rtti X : 81, 82 { }i

In this example, class X inherits only non-polymorphic classes. Class X does not need to be declared rtti.

struct rtti 81 { }i II Non-polymorphic class struct 82 { }i

struct X : 82, Sl {}i II The order is not essential

Applying either __ rtti or using the -RT compiler option will not make a static class into a polymorphic class. See page 155 for a discussion of poly-morphic classes.

II HOW TO GET RUN-TIME TYPE INFORMATION FOR POLYMORPHIC CLASSES.

#include <iostream.h>

#include <typeinfo.h>

Program output Note that type& var, type &var, and type &

var are all equivalent.

116

While in C, you pass arguments only by value; in C++, you can pass arguments by value or by reference. C++ reference types, closely related to pointer types, create aliases for objects and let you pass arguments to func-tions by reference.

The reference declarator can be used to declare references outside functions:

This creates the lvalue ir as an alias for i, provided the initializer is the same type as the reference. Any operations on ir have precisely the same effect as

Borland C++ Programmers Guide

Reference arguments

Implementation 1

Implementation 2

operations on i. For example, ir

=

2 assigns 2 to i, and &ir returns the address of i.

The reference declarator can also be used to declare reference type parameters within a function:

void funcl (int i);

void func2 (int &ir);

int sum=3;

func1 (sum) ; func2 (&sum) ;

II ir is type "reference to int"

II sum passed by value II sum passed by reference

The sum argument passed by reference can be changed directly by func2.

On the other hand, func1 gets a copy of the sum argument (passed by value), so sum itself cannot be altered by func1.

When an actual argument x is passed by value, the matching formal argument in the function receives a copy of x. Any changes to this copy within the function body are not reflected in the value of x itself. Of course, the function can return a value that could be used later to change x, but the function cannot directly alter a parameter passed by value.

The C method for changing x uses the actual argument &x, the address of x, rather than x itself. Although &X is passed by value, the function can access x through the copy of &X it receives. Even if the function does not need to change x, it is still useful (though subject to potentially dangerous side effects) to pass &x, especially if x is a large data structure. Passing x directly by value involves wasteful copying of the data structure.

Compare the three implementations of the function treble:

int treble_l(int n)

{

return 3 * n;

int X, i = 4;

X = treble_l(i);

void treble_2(int* np)

{

*np = (*np ) * 3;

treble_2(int& i);

II X now = 12, i = 4

II i now = 12

Implementation 3 void treble_3(int& n)

{

n = 3 * n;

II n is a reference type

II i now = 36

The formal argument declaration type& t(or equivalently, type& t)

establishes t as type "reference to type." So, when treble_3 is called with the real argument i, i is used to initialize the formal reference argument n. n therefore acts as an alias for i, so n

=

3 *n also assigns 3

*

ito i.

If the initializer is a constant or an object of a different type than the reference type, Borland C++ creates a temporary object for which the reference acts as an alias:

int& ir = 6; 1* temporary int object created, aliased by ir, gets value 6 *1 float f;

int& ir2 = f; 1* creates temporary int object aliased by ir2; f converted before assignment *1

ir2 = 2.0 II ir2 now = 2, but f is unchanged

The automatic creation of temporary objects permits the conversion of reference types when formal and actual arguments have different (but assignment-compatible) types. When passing by value, of course, there are fewer conversion problems, since the copy of the actual argument can be physically changed before assignment to the formal argument.

Im Dokument or an (Seite 124-129)