Four cast conversions in c++, dynamic_cast, static_cast, const_cast, reinterpret_cast

After C++11, there are four types of conversions in C++, namely dynamic_cast, static_cast, const_cast, and reinterpret_cast. Generally, these four casts can be used to replace similar (int) conversions in c code. The following is an introduction to the four mandatory conversions and how to use them.

dynamic_cast

It is mainly used for the inheritance relationship, or to realize the conversion between the polymorphic parent class and the subclass. If the two classes are in an inheritance relationship, but there is no polymorphism (that is, there is no virtual function), then use dynamic_cast to convert, and the compiler will report an error, such as the following code:

#include<iostream>
using namespace std;

class Base  // 父类
{
    
    

};

class Child :public Base {
    
     // 子类

};


int main()
{
    
    
	Base* b_ptr = new Child();

	Child* c_ptr = dynamic_cast<Child*>(b_ptr); //这里会报错,在vs2019中,提示:运行时 dynamic_cast
	// 的操作数必须包含多态类类型

}

In the conversion without polymorphic class type, the compiler will prompt and report an error.
dynamic_cast is mainly used for safety judgment in the process of downlink conversion (conversion from parent class to subclass), and uplink conversion (that is, conversion from subclass to parent class must be safe, such as situation 3 in the following code); if some For unsafe conversion, the return value is nullptr, such as case 2 in the following code; under what circumstances is the downlink conversion safe? For example, when the parent class pointer points to a subclass object, it is safe to convert the parent class pointer to a subclass using dynamic_cast, because the object pointed to by this pointer is a subclass, such as case 2 in the following code.

dynamic_cast application sample code

#include<iostream>
using namespace std;

class Base  // 父类
{
    
    
public:
	virtual void f()
	{
    
    
		cout << "this is base class !" << endl;
	}
};

class Child :public Base {
    
     // 子类
public:
	void f()
	{
    
    
		cout << "this is child class !" << endl;
	}

};


int main()
{
    
    
	//情况1
	Base* b_ptr0 = new Base(); // 指向父类的父类指针
	Child* c_ptr0 = dynamic_cast<Child*>(b_ptr0);// 下行转换,此时由于父类指针实际上是指向父类的,
												//这个转换不安全,所以c_ptr0的值为nullptr
	if (!c_ptr0)
	{
    
    
		cout << "can not cast the type !" << endl; // 代码会走到这里
	}
	else
	{
    
    
		c_ptr0->f();
	}
	//情况2
	Base* b_ptr = new Child();// 指向子类的父类指针
	Child* c_ptr = dynamic_cast<Child*>(b_ptr); // 下行转换,此时由于父类指针实际上是指向子类的
												//所以这个转换是可以的
	if (!c_ptr)
	{
    
    
		cout << "can not cast the type !" << endl;
	}
	else
	{
    
    
		c_ptr->f();// 代码会走到这里
	}
	//情况3
	Child* c_ptr1 = new Child();// 指向子类的子类指针
	Base* b_ptr1 = dynamic_cast<Base*>(c_ptr1); // 将子类转换为父类是安全的,上行转换一定是允许的
	if (!b_ptr1)
	{
    
    
		cout << "can not cast the type !" << endl;
	}
	else
	{
    
    
		b_ptr1->f();// 代码会走到这里
	}
	/*
	* 输出是
	* can not cast the type !
	* this is child class !
	* this is child class !
	* 
	*/

}

static_cast

This kind of static conversion can achieve many things that dynamic cannot achieve, and it cannot be achieved without security checks

  • Conversion between unrelated classes, such as eg1 of the following code

can be realised:

  • Inherit the upstream conversion and downstream conversion between the parent class and the subclass, which does not guarantee safety, such as eg2 of the following code
  • Can perform any conversion from void * to other pointers, such as eg3 of the following code
  • Can convert int float double and enumeration type, such as eg4 of the following code
  • Implement conversion to rvalue references, such as eg5 in the following code

static_cast application sample code

#include<iostream>
using namespace std;

class Other
{
    
    

};

class Base  // 父类
{
    
    
public:
	virtual void f()
	{
    
    
		cout << "this is base class !" << endl;
	}
};

class Child :public Base {
    
     // 子类
public:
	int a = 10;

	string b = string("asd");

	void f()
	{
    
    
		cout << "this is child class !" << endl;
	}

	void CFun()
	{
    
    
		cout << "the first string is " << b[1] << endl;
		cout << "the value of  a in " << a << endl;
	}

};

enum class Fruit {
    
    Anple, Oringe, Banana, Watermelon};


int main()
{
    
    
	//eg 1 不能进行不相关类之间的转换,例如 Other与Base
	Other* o = new Other();
	Base* b = static_cast<Base*>(o); // faild,这里会报错


	//eg 2 能够进行上行转换(upcast)和下行转换(downcast),不保证安全,需要程序员自己去保证安全
	Base* b1 = new Base();
	Child* c1 = new Child();
	Child* c11 = static_cast<Child*>(b1);// 下行转换,不安全,需要程序员自己保证
	//c11->CFun(); // failed 这里调用CFun,由于基类里面没有对应a变量和b变量,所以肯定会报错
	Base* b11 = static_cast<Base*>(c1); // 上行转,安全


	//eg 3 能够进行void * 到其他指针的任意转换
	void* p = nullptr;
	Base* b2 = static_cast<Base*>(p); // 这里是将void * 转换为 Base*
	void* p1 = static_cast<void*>(b1); // 这里是将Base* 转换为 void *


	// eg4 能够将 int float double以及枚举类型的转换
	Fruit f = Fruit::Anple;
	int i_value = static_cast<int>(f);
	Fruit ff = static_cast<Fruit>(3); // 将整数3转换为Fruit::Watermelon
	int i_val = 100;
	double d_val = 1.000;
	float f_val = 2.000;
	int i_value1 = static_cast<int>(d_val); // double 向 int 进行转换
	int i_value2 = static_cast<int>(f_val); // float 向 int 进行转换
	float f_val1 = static_cast<float>(i_val);// int 向 double 进行转换
	double d_val1 = static_cast<double>(i_val);// int 向 float 进行转换

	// eg5 实现转换到右值引用
	int&& left_val = static_cast<int&&>(5); // 将左值转换为右值,和std::move功能差不多

}

const_cast

It is mainly used to remove const attributes in composite types, such as the following code:

#include <iostream>
#include <stdio.h>

using namespace std;

void fun(int* a)
{
    
    
    cout << *a << endl;
}

int main()
{
    
    
    int a = 10;
    const int* a_ptr = &a;
    //fun(a_ptr); // failed, 参数类型不对,需要非const 类型的参数
    fun(const_cast<int*>(a_ptr)); // success,转换为非const 类型的参数,可以进行函数调用

    return 0;
}

In our custom class , after removing the const attribute, the original value can be modified, usually by converting it to a pointer or reference, such as the following example:

#include <iostream>
#include <stdio.h>

using namespace std;

void fun(int* a)
{
    
    
    cout << *a << endl;
}

class myClass
{
    
    
public:
    int a = 10;
};

int main()
{
    
    
    const myClass* c = new myClass();
    cout << c->a << endl;
    //c->a = 100; // failed,const常量不能修改
    myClass* cc = const_cast<myClass*>(c);
    cc->a = 100; // success
    cout<< c->a <<" "<<cc->a<< endl;
    return 0;
}

However, the above emphasizes the custom class, if it is not a custom class, such as integer, string pointer, etc., there will be unexpected errors or strange phenomena, for example: eg1
:

#include <iostream>
#include <stdio.h>

using namespace std;

int main()
{
    
    
    const char * s = "asd asd";
    char * ss = const_cast<char *>(s);
    ss[1] = '1'; // 这里会报错,
    cout << s << '\n' << ss << endl;
    return 0;

}

eg2:
borrowed from this article, the explanation is quite clear

#include <iostream>
using namespace std;
 
int main () {
    
    
	const int data = 100;
    int *pp = (int *)&data;
    *pp = 300;
    cout << "data = " << data << "\t地址 : " << &data << endl << endl ;
	cout << "  pp  = " << *pp << "\t地址 : " << pp << endl << endl ;
	int *p = const_cast<int*>( &data ) ;
	cout << "data = " << data << "\t地址 : " << &data << endl << endl ;
	cout << "  p  = " << *p << "\t地址 : " << p << endl << endl ;
	*p = 200 ;
 
	cout << "data = " << data << "\t地址 : " << &data << endl << endl ;
	cout << "  p  = " << *p << "\t地址 : " << p << endl << endl ;
 
	return 0 ;
}

The output is
insert image description here
weird? The address of data is 0x6ffdfc, and the address pointed to by p is also 0x6ffdfc, but after modifying p, the contents of the same address are different.
May be const problem? The mechanism of const is to replace data with a constant during compilation. This approach is called constant folding.
Constant folding is related to the working principle of the compiler and is a kind of compilation optimization of the compiler. When the compiler performs grammatical analysis, it calculates and evaluates the constant expression, replaces the expression with the obtained value, and puts it into the constant table. So in the above example, during the optimization process, the compiler will replace all encountered data (const constants) with the content 100, which is similar to macro replacement. Constant folding only works on native types, not on our custom types.

But usually what we use is to modify the class defined by ourselves. If it is a native type, the general operation will not modify the corresponding data, and most of it is just used as a parameter.

reinterpret_cast

Compared with static_cast, reinterpret_cast is more powerful, less secure, and has higher requirements for programmers. It can achieve

  • Conversion between related types, such as two completely unrelated classes A and B, such as eg1 in the following sample code;
  • Conversion between pointers and integers can be realized, such as eg2 in the following sample code;
#include<iostream>
using namespace std;

class Other
{
    
    

};

class Base  // 父类
{
    
    
public:
	virtual void f()
	{
    
    
		cout << "this is base class !" << endl;
	}
};

class Child :public Base {
    
     // 子类
public:
	int a = 10;

	string b = string("asd");

	void f()
	{
    
    
		cout << "this is child class !" << endl;
	}

	void CFun()
	{
    
    
		cout << "the first string is " << b[1] << endl;
		cout << "the value of  a in " << a << endl;
	}

};


int main()
{
    
    
	//eg 1 可以进行不相关类之间的转换,例如 Other与Base
	Other* o = new Other();
	Base* b = reinterpret_cast<Base*>(o); 

	//eg 2 指针和整数进行转换
	int i_val = 100;
	int* i_ptr = &i_val;
	int reinterpret_val = reinterpret_cast<int>(i_ptr); // 将整数指针转换为整数
	cout << reinterpret_val << endl;

	int* i_ptr1 = reinterpret_cast<int *>(reinterpret_val); // 将整数转换为整数指针
	cout << *i_ptr1 << endl;

	Base* b1 = new Base();
	int reinterpret_val1 = reinterpret_cast<int>(b1); // 将Base指针转换为整数
	cout << reinterpret_val1 << endl;
	Base* b2 = reinterpret_cast<Base*>(reinterpret_val1); // 将对应整数转换为Base指针
	b2->f(); // 输出this is base class !


}

Summarize

  • dynamic_cast is mainly used in the conversion of polymorphic classes to ensure safe conversion.
  • Static_cast cannot perform conversion between unrelated classes and can realize up-conversion and down-conversion; inherit up-conversion and down-conversion between parent class and subclass, which does not guarantee safety; can perform arbitrary conversion from void * to other pointers; can convert Conversion of int float double and enumeration type; realize conversion to rvalue reference;
  • const_cast is usually used to remove the const attribute of a variable. For native types, it may be troublesome to modify the corresponding value, but for most types, it can be modified, usually by converting to a pointer or reference;
  • reinterpret_cast is mainly used for conversion between two completely unrelated types, and can realize conversion between pointers and integers;

Guess you like

Origin blog.csdn.net/qq_41841073/article/details/127031343