[C++] Talking about mandatory type conversion (4 types)

01. C-style type conversion

When learning C language, we will use mandatory type conversion in many occasions. At that time, we seldom consider whether there is a problem with this, because this is the bad part of C language, and we can handle everything , therefore, the errors are varied.

C-style coercion (Type Cast) is easy to understand, no matter what type of conversion can use the following method.

int nCarNum = 10;
double fCarWeight = 22.3;

int tmp = (int)fCarWeight;  //潜在问题:精度丢失

int a;
char b = 'b';
a = b; //隐式转换

//亦或是

const char* buf = "ABCD";
char* tp = buf;  //报错:不存在由const char* 到 char* 的转换,所以就有了下面的表达
char* tp = (char*)buf;  //OK,当然,这个没有大问题,是可以这样的,但是如果你要去修改这个buf的值就会出现问题了

C style looks like this equation: template A = (template)B;(conversions shown)

02. C++ four types of mandatory conversion functions

2.1、 static_cast

Generally speaking, we call this method a static conversion, which looks like: static_cast<type-id> (expression), and is the most common of these four conversions, but it has a very buggy problem, and it does not perform runtime type checking at runtime to ensure the safety of the conversion This operator converts expression to type-id type

The main usage is as follows:

  • Used for conversion of pointers or references between base classes (parent classes) and derived classes (child classes) in a class hierarchy.

Upcasting (converting pointers or references of derived classes to base class representations) is safe;
        downcasting (converting base class pointers or references to derived class representations) is unsafe because there is no dynamic type checking .

  • Used for conversion between basic data types, such as converting int to char, and converting int to enum. The security of this conversion should also be guaranteed by the developer.

  • converts a null pointer to a null pointer of the target type

  • Convert any type of expression to void type

    Examples are as follows:

    //第一种:类层次中的转换
    Class B: public A
    {
          
          
    	//略
    }
    A* p1 = new A();
    B* p2 = new B();
    p1 = static_cast<A*>(p2)  //类型安全,反过来就不一定了,使用时,一定要考虑到
    
    //第二种:基础数据类型转换
    typedef enum DATATYPE
    {
          
          
    	DATATYPE_ONE = 0,
    	DATATYPE_TWO = 1,
    }TYPE;
    
    int a;
    TYPE type;
    a = static_cast<int>(type);  //OK
    
    //第三种:空指针转目标类型的空指针
    int* p1 = NULL;
    DataPos* b = NULL;  //自定义类型
    //将int* 转 DataPos*
    b = static_cast<DataPos*>(p1);  //OK
    
    //第四种:任意类型表达式转void类型
    DataPos* dp = new DataPos();
    
    void* buf = static_cast<void*>(dp);  //OK
    

Many friends may ask, can the C method be used in C++?

If it is written in Visual Studio and Resharper C++ is installed, if it is written in C style, it will prompt you and help you automatically modify it to C++ style, as follows:
C-style cast used instead of a C++ cast

It should be noted that static_cast cannot convert the const, volitale or __unaligned attributes of expression, as shown in the following figure:
insert image description here

2.2、 const_cast

The above static_cast cannot convert const int* to int*, but const_cast can be used const_cast<type-i> (expression). As the following code:

	const int a = 10;
	const int * p = &a;
	*p = 20;						 // Compile error: Cannot assign readonly type 'int const'
	int res1 = const_cast<int>(a);   // Compile error: Cannot cast from 'int' to 'int' via 		const_cast
									 // only conversions to reference or pointer types are allowed
	int* res2 = const_cast<int*>(p); // OK

In other words, the content inside const_cast<> must be a reference or a pointer, even converting an int to an int will not work.
For the use of const_cast, there are actually many things that I don’t understand (C++ undefined behavior), as follows:

const int a = 11;
const int *p1 = &a;
int* p2 = const_cast<int*>(p1);
*p2 = 20;  //OK,修改成功

cout << "a: " << a << "    &a :" << &a << endl;  // a: 11     &a: ********
cout << "*p1: " << *p1 << "    p1 :" << p1 << endl;  //*p1:20   p1: *********
cout << "*p2: " << *p2 << "    p2 :" << p2 << endl;  //*p2: 11   p2: *********

//其中,***********代表地址,地址是相同的,重点看值

This involves a compiler optimization step, that is, the constant compiler will automatically optimize it away.
In VS2017, in debug mode, after executing *p = 20; this step, the value of a becomes 20, but the output a and *p are both 11. What I understand is that the variable of const int type is optimized by the compiler, and all the individual a in it have become 11, so it will not be affected by any modification. This situation includes: 1. Directly use constants such as 11 to initialize a, 2. Use c which is also of const int type to initialize a. If a is initialized with c such as int c = 11;, then a is variable.

2.3、 reinterpret_cast

reinterpret_cast has three main casting uses:

  • Change the type of a pointer or reference
  • Converts a pointer or reference to an integer of sufficient length
  • Converts an integer type to a pointer or reference type

Usage: reinterpret_cast <type-id> (expression)
type-id must be a pointer, reference, arithmetic type, function pointer or member pointer. It can convert a pointer to an integer, or convert an integer to a pointer (first convert a pointer to an integer, then convert the integer to a pointer of the original type, and get the original pointer value).
  The types we map to just for cryptic and other purposes are the most dangerous of all mappings. (This sentence is the original words in C++ programming thinking). Therefore, you need to use reinterpret_cast with caution.

The example is as follows (example from: a hash function auxiliary of MSDN):

//expre_reinterpret_cast_Operator.cpp
//compile with: /EHsc
#include <iostream>

//Returns a hash code based on an address
unsigned short Hash(void* p){
    
    
	unsigned int val = reinterpret_cast<unsigned int>(p);
	return (unsigned short)(val ^ (val >> 16));
}

using namespace std;
int main()
{
    
    
	int a[20];
	for(int i = 0; i < 20; i++)
		cout << Hash(a+i) << endl;
}

In the above example, reinterpret_castconvert void* to unsigned int type data.

2.4、dynamic_cast

The usage is: dynamic_cast<type-id> (expression)
several characteristics are as follows:

  • The other three are completed at compile time, dynamic_cast is processed at runtime, and type checking is required at runtime
  • cannot be used for casts of built-in primitive data types
  • dynamic_cast requires that the target type described in <> must be a pointer or a reference. If the dynamic_cast conversion is successful, it will return a pointer or reference to the class, and if the conversion fails, it will return nullptr.
  • During class conversion, when performing up-conversion between class levels (subclass pointer to parent class pointer), dynamic_cast and static_cast have the same effect. When performing downcast (conversion of parent class pointer to subclass pointer), dynamic_cast has the function of type checking, which is safer than static_cast. The success of the downcast is also related to the type to be converted, that is, the actual type of the object pointed to by the pointer to be converted must be the same as the converted object type, otherwise the conversion fails. In C++, type conversion at compile time may cause errors at runtime, especially when pointer or reference operations involving class objects are involved, errors are more likely to occur. The Dynamic_cast operator can test possible problematic type conversions at runtime.
  • If dynamic_cast is used for conversion, there must be a virtual function in the base class, otherwise the compilation will not pass (the existence of a virtual function in the class means that it wants to make the base class pointer or reference point to the derived class object, and only then can the conversion significance). This is because runtime type checking requires runtime type information, and this information is stored in the virtual function table of the class. Only classes that define virtual functions have a virtual function table (the basic principle of virtual functions in C++ is written in this article Good, https://blog.csdn.net/xiejingfa/article/details/50454819
class base {
    
    
public:
	void print1() {
    
     cout << "in class base" << endl; }
};

class derived : public base {
    
    
public:
	void print2() {
    
     cout << "in class derived" << endl; }
};

int main() {
    
    
	derived *p, *q;
	// p = new base;	//  Compilr Error: 无法从 "base * " 转换为 "derived * "

	// Compile Error: Cannot cast from 'base*' to 'derived*' via dynamic_cast: expression type is not polymorphic(多态的)
	// p = dynamic_cast<derived *>(new base);

	q = static_cast<derived*>(new base);	// ok, but not recommended

	q->print1();	// in class base
	q->print2();	// in class derived
}

As can be seen from the above code 用一个派生类的指针是不能直接指向一个基类的对象, there will be compilation errors. If you use dynamic_cast, you will also get a compilation error, prompting us 基类不是多态的,也就是基类中没有虚函数. It can be seen that static_cast can be compiled and passed, and the output results look correct, but VS will still prompt thatDo not use static_cast to downcast from a base to a derived class

Static_cast does not have the function of guaranteeing type safety during forced type conversion, but dynamic_cast provided by C++ can solve this problem. dynamic_cast can detect whether type conversion is type safe when the program is running. Of course, dynamic_cast is also conditional when used, it requires that the converted expression must contain a polymorphic class type (that is, a class that contains at least one virtual function)

class A {
    
    
public:
	virtual void print(){
    
    
		cout << "in class A" << endl;
	};
};

class B :public A {
    
    
public:
	void print(){
    
    
		cout << "in class B" << endl;
	};
};

class C {
    
    
	void pp(){
    
    
		return;
	}
};

int main() {
    
    
	A *a1 = new B; // a1是A类型的指针指向一个B类型的对象
	A *a2 = new A; // a2是A类型的指针指向一个A类型的对象
	B *b1, *b2, *b3, *b4;
	C *c1, c2;
	b1 = dynamic_cast<B*>(a1);	// not null,向下转换成功,a1 之前指向的就是 B 类型的对象,所以可以转换成 B 类型的指针。
	if (b1 == nullptr) cout << "b1 is null" << endl;
	else               cout << "b1 is not null" << endl;

	b2 = dynamic_cast<B*>(a2);	// null,向下转换失败
	if (b2 == nullptr) cout << "b2 is null" << endl;
	else               cout << "b2 is not null" << endl;

	// 用 static_cast,Resharper C++ 会提示修改为 dynamic_cast
	b3 = static_cast<B*>(a1);	// not null
	if (b3 == nullptr) cout << "b3 is null" << endl;
	else               cout << "b3 is not null" << endl;

	b4 = static_cast<B*>(a2);	// not null
	if (b4 == nullptr) cout << "b4 is null" << endl;
	else               cout << "b4 is not null" << endl;

	a1->print();	// in class B
	a2->print();	// in class A
	
	b1->print();	// in class B
	// b2->print(); // null 引发异常
	b3->print();	// in class B
	b4->print();	// in class A

	c1 = dynamic_cast<C*>(a1);	// 结果为null,向下转换失败
	if (c1 == nullptr) cout << "c1 is null" << endl;
	else               cout << "c1 is not null" << endl;

	// c2 = static_cast<C*>(a1);	// 类型转换无效, Cannot cast from 'A*' to 'C*' via static_cast
	// delete 省略
}

03. Summary

Regarding type conversion, C++ provides four methods. These four methods use different functions according to different scenarios. The summary is as follows: 1. static_cast: type conversion does not check security,
but basically allows you to convert, But const objects or const members are not allowed.
2. const_cast: Makes up for the above-mentioned inability to convert constant objects and constant members, but const is generally not recommended for conversion and modification. In summary, const_cast is usually a helpless move, but C++ provides a way to modify const variables, but This method is not of any substantial use, it is better not to use it. Don't let const variables change.
3. reinterpret_cast: type-id must be a pointer, reference, arithmetic type, function pointer or member pointer, and during the conversion, the mapping is prone to errors and is very dangerous.
4. dynamic_cast: dynamic type conversion, security will be checked during compilation, but there is a prerequisite that the class with virtual functions can be converted, which depends on the implementation of the virtual function table.

Guess you like

Origin blog.csdn.net/m0_43458204/article/details/119183801
Recommended