web developer & system programmer

coder . cl

ramblings and thoughts on programming...


c++ type casting operators

published: 25-07-2010 / updated: 25-07-2010
posted in: c++, programming, tips
by Daniel Molina Wegener

Traditionally, C programmers and Java programmers are accustomed to use the type casting form: (type)variable. C++, instead of that classical type casting form, has various type casting operators, used for different purposes. Each operator is used with different kinds of references, for example, you can not use the static_cast operator with virtual classes. Type casting operators in C++ has a syntax very similar to C++ Templates: cast_operator<type>(variable).

const_cast operators

If you want an expression to being const, just use the const_cast operator. For example if a legacy function written in C requires of a const argument, just pass a non-const argument by using the const_cast operator:

extern "C" {
    void my_function(const char *p);
};

std::string s = std::string("hello world");
my_function(const_cast<char *>(s.c_str()));

static_cast operator

The static_cast operator performs the conversion unconditionally, without verifying data types at compile time. This makes this operator quite dangerous, so, you need to verify data types before you use variables in this kind of type casting.

if (typeid(*ptr_a) == typeid(MyClass)) {
    ptr_b = static_cast<MyClass>(ptr_a);
    ptr_b->applySomeMethod();
}

You can use the typeid C++ builtin to verify the data type. Also, you must consider that this operator is valid only if the conversion is explicit. Suppose Parent is a base class to Child and that Unrelated is an unrelated class. Then conversions from Child to Parent are valid, but a conversion from Child to Unrelated is disallowed:

Parent a;
Child b;
Parent *a_1 = static_cast<Parent *>(&b); // valid upcast
Unrelated *c_1 = static_cast<Unrelated *>(&b_1); // invalid, Unrelated

The best usage for the static_cast opertaor, are numerical conversions, so you can cast from int, double, float and others of similar type to another int, double, float and similar types, including enum types. Some compilers does this kind of conversion explicit, but more strict compilers usually throw the proper warning on this kinds of assignment, without the proper type casting operation (line 4).

float a;
int b;
a = static_cast<float>(b);
a = b;

dynamic_cast operator

The dynamic_cast operator, among other things, does a runtime check over types applied on the cast operation. As the above example of static_cast operator, dynamic cast does the typeid() checking. Then, if you have a pointer or reference to an object whose declared type is a base class, but you need to obtain a derived class pointer or reference, you should use dynamic_cast, since the dynamic_cast operator is another RTTI (Run-time type information) operator. The use of dynamic_cast derives into more overhead of your program, so you must use it with moderation.

if (ptr_b == static_cast<MyClass>(ptr_a);) {
    ptr_b->applySomeMethod();
}

If the type checking and conversion fails, the operator returns a non-null reference, else it will return a null reference (zero). Also, some compilers will advice on the proper usage of the dynamic_cast operator, the more strict ones, will throw an error.

Parent a;
Child b;
Parent *a_1 = dynamic_cast<Parent *>(&b); // valid upcast
Unrelated *c_1 = dynamic_cast<Unrelated *>(&b_1); // invalid, returns NULL

reinterpret_cast operator

This — possibly — is the most risky to use type casting operator. It completely transforms a pointer to very different types. For example:

some_struct_s {
    short a;
    short b;
};
typedef struct some_struct_s some_struct_t;
some_struct_t *s;
unsigned long v = 0xd34db33f;
s = reinterpret_cast<some_struct_t *>(&v);
std::cout << s->a; // display first 2 bytes of value

As the example above, the pointer was completely transformed to different kind, with the dangerous assumption that the platform has a byte alignment that make possible to access the first to bytes of some address using this kind of operation. This cast is not portable, some compilers should throw some error about it.

The fact is that the reinterpret_cast operator overrides the initial declaration of the given variable, and makes possible to that variable to being reinterpreted as a different one. The usage of the reinterpret_cast must be done careful, for example for derived to base class reinterpretation.

BaseClass a;
DerivedClass *c = reinterpret_cast<DerivedClass *>(&a);

Since many of this kind of conversions are not possible in all compilers, you must try to setup your compiler to the most pedantic flags that it can allow. For example on GCC, you should use -Wall -Wextra -Wshadow -pedantic -std=c++98 as compiler flags. It will keep your code portable and will allow you to create more standarized programs.

Here is a full compiling example of the core ideas in this post:


class A {
public:
	A();
	~A();
};

class B : public A {
public:
	B();
	~B();
};

A::A() {
}

A::~A() {
}

B::B() : A() {
}

B::~B() {
}


int
main(int argc, char **argv)
{

	A *ptr_a = new A();
	B *ptr_b = new B();

	A *a_1 = static_cast<A *>(ptr_b);
	B *b_1 = static_cast<B *>(ptr_a);

	A *a_2 = dynamic_cast<A *>(ptr_b);

	A *a_3 = reinterpret_cast<A *>(ptr_b);
	B *b_3 = reinterpret_cast<B *>(ptr_a);

	const A *a_4 = const_cast<A *>(ptr_a);
	const B *b_4 = const_cast<B *>(ptr_b);

	delete ptr_a;
	delete ptr_b;

	return 0;

}


No coments yet.

post a comment

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>