Intro
Casting in C++ is a way to convert a value from one data type to another. There are four casting operators in C++, each serving different purposes and with different levels of safety and functionality.
This paper will introduce when and how to use them with examples.
std::static_cast
It is the most commonly used cast in C++. It is used for conversions between compatible types, you can think of it as converting between basic c++ types. Such as int, float, double. You can use it convert integer to any enum type.
int i = 10;
float f = static_cast<float>(i); // Converts int to float
# int to enum, very useful
enum EnumType {ONE, TWO};
EnumType e = static_cast<EnumType>(0);
Also, you can use static_cast for downcasting, but you need to know that the object to be converted can indeed be converted. That’s why it’s called static.
So, use static_case convert base class to derived class is not recommended. Please use dynamic_cast.
std::dynamic_cast
Safely converts pointers and references to classes up, down, and sideways along the inheritance hierarchy.
Used primarily with pointers or references to classes in a polymorphic hierarchy. It checks the type at runtime to ensure the validity of the cast. If the cast fails (e.g., when casting to a wrong class type), it returns nullptr for pointers or throws an exception for references.
Dynamic_cast is very safe, because it will return nullptr if failed. It always used in interface design.
Base* b = new Derived();
Derived* d = dynamic_cast<Derived*>(b); // Safe downcasting
Base* b2 = dynamic_cast<Base*>(d); // Safe upcasting
std::const_cast
const_cast is simple, only used to add or remove the const qualifier from a variable. It’s the only C++ cast that can do this.
const int i = 10;
int* pi = const_cast<int*>(&i); // Removes const qualifier
int i = 10;
auto ci = const_cast<const int*>(&i); // Add const qualifier
# change the const function parameter value
void test(const int* i) {
int* pi = const_cast<int*>(i);
pi ++
}
std::reinterpret_cast
Just like C-style type convert, you need to be very careful when using it.
It’s always used to converting buffer into struct, which can speed up protocol parsing. In this case, please make sure struct’s elements align as you wish.
# In c++, the default alignment of a struct is determined by the alignment requirements of its largest member
typedef struct _UnPackedType {
uint8_t a;
double b;
uint16_t c;
} UnPackedType;
UnPackedType up = *(reinterpret_cast<UnPackedType*>(buf)) // element value in struct maybe wrong
typedef struct _PackedType {
uint8_t a;
double b;
uint16_t c;
} __attribute__((packed)) PackedType;
PackedType up = *(reinterpret_cast<PackedType*>(buf)) // correct