(c + + growth record) - C + + cast operators (static_cast, reinterpret_cast, const_cast and dynamic_cast)

Posted by batfastad on Sat, 19 Feb 2022 01:50:39 +0100

C + + cast operator

appendix

Encyclopedia of professional vocabulary

  1. C language
  2. C++
  3. Pointer
  4. Cast type

reference

Statement: some of the contents of this article are directly from references and are subject to infringement and deletion.

  1. C + + cast operators (static_cast, reinterpret_cast, const_cast and dynamic_cast)

summary

In my daily development process, I often use some type conversion functions, mainly the upstream conversion from subclass to parent class or the downstream conversion from parent class to child class, as well as some forced data type conversion. These are some common contents we often encounter in our daily development work, so I have a whim today, I want to summarize some skills and cases about the use of type converters in C + +, hoping to help others and record and review my knowledge.

Type conversion

Concept introduction

The way to cast the primitive type is to cast the operator name as the primitive type C language Old-fashioned practices, C++ Reserved for compatibility.

    C++ Four cast operators with different functions are introduced for cast: static_cast,reinterpret_cast,const_cast and dynamic_cast.

Cast type conversion has certain risks. Some conversions are not necessarily safe. For example, convert int integer value to an integer value Pointer Type. It may fail to convert a base class pointer into a derived class pointer. There may be a mismatch when converting a function pointer into another function pointer. Converting a constant pointer into a non constant pointer may lead to the destruction of the original constant. It is not very safe. C + + introduces a new forced type conversion mechanism mainly to overcome C language Cast has the following three disadvantages.

  1. It does not formally reflect the difference of conversion function and risk.
  2. When converting a polymorphic base class pointer to a derived class pointer, the security is not checked, that is, it is impossible to judge whether the converted pointer actually points to a derived class object.
  3. It is difficult to find out in the program where the cast is performed.

For example, there is no risk of converting int to double. This is to convert a simple type to a complex type, but converting a complex type to a simple type risks losing precision. For example, if you want to convert double back to int, there is a risk of precision loss, and convert a constant pointer to a non constant pointer, Converting a base class pointer to a derived class pointer is a high-risk operation, and the latter two bring different risks (that is, they may cause different kinds of errors), which are not distinguished by the forced type conversion form of C language.

In many cases, we use cast in conjunction with Assert macro to verify whether it is the program crash caused by cast, because cast is an inducement that often causes program crash. For a program, if we have always been used to using the default form of cast in C language, Use (int)xxx. If you encounter a bug or program crash suspected to be caused by forced type conversion, you want to check the correctness of a conversion. It's hard to locate it in the program. You don't know which conversion caused the problem, so it's not easy to search the corresponding location.

If you switch to C + +, you only need to find_ Just cast string. You can even find a specific type of cast according to the type of error. For example, it is suspected that an error may be due to the use of reinterpret_ If it is caused by cast, you can only find reinterpret_cast string.

The usage of C + + cast operator is as follows:

Cast operator < type to convert > (expression to be converted)

For example:

double d = static_cast <double> (3 * 5);  //Convert the value of 3 * 5 to a real number
float f = static_cast<float> (2 * 3);

class Base 
{
public:
	Base();
	void funcBase();
};

class Child : public Base
{
public:
	Child();
	void funcChild();
}

int main()
{
	Child a;
	Base* b = static_cast<Base*>(b);
	// b->funcChild();  // error b can't use the child method.
	a->funcBase();		// ok
	a->funcChild();		// ok
	Base b1;
	Child c = static_cast<Child*>(b1);
	c->funcBase();		// ok
	// c->funcChild();		// error c can't use the child method. 
}

Comparison of similarities and differences

static_cast

     static_cast is used for "natural" and low-risk conversions, such as conversion between integer, floating-point and character types. In addition, if the class to which the object belongs overloads the cast operator T (for example, t is int, int * or other type name), static_cast can also be used for object to T type conversion.

     static_cast cannot be used to convert between pointers of different types, between integers and pointers, or between references of different types. Because these are high-risk conversions.

     static_ Examples of cast usage are as follows:

///
// Copyright (c)2021, Tom Zhao personal. ("TZOpenTools")
// This software is a personal tools project by Tom Zhao.
// Description:
///

#include <iostream>

using namespace std;

class A
{
public:
	operator int() { return 1; }
	operator char*() { return NULL; }
};

int main()
{
	A a;
	int n;
	const char* p = "This is a str for static_cast";
	n = static_cast <int> (3.14);		// The value of n becomes 3
	n = static_cast <int> (a);				// Call a.operator int, and the value of N becomes 1
	p = static_cast <char*> (a);			// Call a.operator char *, and the value of p becomes NULL
	// n = static_cast <int> (p); 			//  Compilation error, static_cast cannot convert a pointer to an integer
	// p = static_cast <char*> (n); 		//  Compilation error, static_cast cannot convert an integer to a pointer
	return 0;
}

reinterpret_cast

     reinterpret_cast is used to convert between different types of pointers, between different types of references, and between pointers and integer types that can accommodate pointers_ During cast conversion, the process is bit by bit replication.

This transformation provides strong flexibility, but the security of transformation can only be guaranteed by the care of programmers. For example, programmers insist on converting an int * pointer, function pointer or other types of pointer into a pointer of string * type. As for the error caused by calling the member function of string class with the converted pointer in the future, Programmers can only undertake the tedious work of finding errors by themselves: (the C + + standard does not allow the conversion of function pointers to object pointers, but some compilers, such as Visual Studio 2010, support this conversion).

     reinterpret_ Examples of cast usage are as follows:

///
// Copyright (c)2021, Tom Zhao personal. ("TZOpenTools")
// This software is a personal tools project by Tom Zhao.
// Description:
///

#include <iostream>

using namespace std;

class A
{
public:
	int i;
	int j;
	A(int n) :i(n), j(n) { }
};

int main()
{
	A a(100);
	int &r = reinterpret_cast<int&>(a);				// Force r to refer to a
	r = 200;															// Turn a.i into 200
	cout << a.i << "," << a.j << endl;					// Output 200100
	int n = 300;
	A *pa = reinterpret_cast<A*> (&n);				// Force pa to point to n
	pa->i = 400;													// n becomes 400
	pa->j = 500;													// This statement is unsafe and is likely to cause the program to crash
	cout << n << endl;											// Output 400
	long long la = 0x12345678abcdLL;
	pa = reinterpret_cast<A*>(la);						// la is too long, only copy the lower 32-bit 0x5678abcd to pa
	unsigned int u = reinterpret_cast<unsigned int>(pa);	// Copy pa bit by bit to u
	cout << hex << u << endl;								// Output 5678abcd
	typedef void(*PF1) (int);
	typedef int(*PF2) (int, char *);
	PF1 pf1 = nullptr; 
	PF2 pf2;
	pf2 = reinterpret_cast<PF2>(pf1);					// Two different types of function pointers can be converted to each other
}

The operation results are as follows:



In the process of compiling, there will be a truncation prompt of forced conversion. Forced conversion is not recommended for some types.

The code in line 19 is not safe, because in the compiler's view, the storage location of pa - > J is the four bytes after n. This statement will write 500 to these four bytes. But I don't know what these four bytes are used to store. Writing them rashly may lead to program errors and even crash.

All the transformations in the above program have no practical significance, just to demonstrate reineibret_ Just the use of cast. This weird transformation may be used when writing hacker programs, viruses or anti-virus programs.

     reinterpret_cast embodies the design idea of C + + Language: users can do any operation, but they should be responsible for their own behavior.

const_cast

     const_ The cast operator is only used to remove the const attribute. It is also the only operator among the four cast operators that can remove the const attribute.

Convert const references to non const references of the same type. Const can be used when converting const pointers to non const pointers of the same type_ Cast operator. For example:

const string s = "Inception";
string& p = const_cast <string&> (s);
string* ps = const_cast <string*> (&s);  // &The type of S is const string*

///
// Copyright (c)2021, Tom Zhao personal. ("TZOpenTools")
// This software is a personal tools project by Tom Zhao.
// Description:
///

#include <iostream>
#include <string>

using namespace std;

class A
{
public:
	const double i = 5.0;
	const int j = 10;
	const string m_s = "Test String.";
	float f = 2.0f;
};

int main()
{
	A a;
	cout << a.i << '\t' << a.j << '\t' << a.m_s << endl;
	string& p_str = const_cast<string&> (a.m_s);
	p_str = "New Test String!.";
	cout << a.i << '\t' << a.j << '\t' << a.m_s << endl;
	cout << p_str << endl;
	string* ps = const_cast<string*>(&a.m_s);
	*ps = "Point Test String";
	cout << a.i << '\t' << a.j << '\t' << a.m_s << endl;
	cout << ps << '\t' << *ps << endl;
	// int& p_ i = const_ cast<int&>(a.i); //   It is not allowed to modify the const of the basic type, only the type qualifier
	// p_i = 200;

	const A ca;
	A& pa = const_cast<A&>(ca);
	pa.f = 30.0f;
	cout << ca.i << '\t' << ca.j << '\t' << ca.m_s << '\t' << ca.f << endl;
	cout << pa.i << '\t' << pa.j << '\t' << pa.m_s << '\t' << pa.f << endl;
}

dynamic_cast

reinterpret_cast can cast the pointer of the polymorphic base class (the base class containing virtual functions) to the pointer of the derived class, but this conversion does not check the security, that is, it does not check whether the converted pointer actually points to a derived class object. dynamic_cast is specially used to cast the pointer or reference of polymorphic base class into the pointer or reference of derived class, and can check the security of the conversion. For unsafe pointer conversion, the conversion result returns a NULL pointer.

     dynamic_cast ensures security through "runtime type checking". dynamic_cast cannot be used to cast a pointer or reference of a non polymorphic base class into a pointer or reference of a derived class -- this conversion cannot guarantee security, so we have to use reinterpret_cast to complete.

     dynamic_ The example program of cast is as follows:

///
// Copyright (c)2021, Tom Zhao personal. ("TZOpenTools")
// This software is a personal tools project by Tom Zhao.
// Description:
///

#include <iostream>
#include <string>

using namespace std;

class Base
{  
//There are virtual functions, so it is a polymorphic base class
public:
	virtual ~Base() {}
};

class Derived : public Base { };

int main()
{
	Base b;
	Derived d;
	Derived* pd;
	pd = reinterpret_cast <Derived*> (&b);
	if (pd == NULL)
		//pd will not be NULL here. reinterpret_cast does not check security and always performs conversion
		cout << "unsafe reinterpret_cast" << endl; //Will not execute
	pd = dynamic_cast <Derived*> (&b);
	if (pd == NULL)  //The result will be NULL because &b it does not point to a derived class object and this conversion is unsafe
		cout << "unsafe dynamic_cast1" << endl;  //Will execute
	pd = dynamic_cast <Derived*> (&d);  //Secure conversion
	if (pd == NULL)  //pd will not be NULL here
		cout << "unsafe dynamic_cast2" << endl;  //Will not execute
	return 0;
}


In line 27, you can know whether the conversion in line 26 is safe by judging whether the value of pd is NULL. The same goes for line 34.

If the following statement appears in the above program:

Derived & r = dynamic_cast <Derived &> (b);

How to judge whether the conversion is safe?

There is no empty reference, so you can't judge whether the conversion is safe by the return value. The solution of C + + is dynamic_ When cast casts a reference, if it finds that the conversion is unsafe, it will throw an exception. By handling the exception, it can find the unsafe conversion.

Summary

The following opinions mainly come from my personal coding habits, and the conclusions are for reference only. I can discuss and study. After all, I am also very good at cooking.

  1. Do not use type conversion, try not to use type conversion, and use only the original subclass types as much as possible.
  2. When you need to use type conversion, you can try to avoid using reinterpret_cast because he is too free and security is too poor.
  3. Can you not force const_ In the case of cast conversion, try not to destroy the properties of constants.
  4. Can use static_ Don't use dynamic in the place of cast_ cast .

Personal Maxim

Feel the life you need to stick to with your heart, and the future will slowly give you the answer.

Topics: C C++ Programming pointer Polymorphism