C++语言进阶:xxx_cast

1 xxx_cast类型转换

  • 隐式类型转换
  • 显式类型转换

语法:

xxx_cast <类型> (表达式)

1.1 static_cast

相当于C语言中的强制转换,用于非多态类型之间的转换,不提供运行时的检查来确保转换的安全性。

主要有如下:

  • 1、基本数据类型转换
  • 2、int转换成enum
  • 3、基类和子类之间指针和引用的转换
    3.1、上行转换,把子类的指针或引用转换成父类,这种转换是安全的(通常使用默认转换)。
    3.2、下行转换,把父类的指针或引用转换成子类,这种转换是不安全的,也需要程序员来保证(通常使用dynamic_cast)。

1.1.1 基本数据类型转换

  • int转换成char

int n = 97;
cout << n << '\t' << (char)n << '\t' << static_cast<char>(n) << endl;

  • int转换成float

int n = 1;
cout << n/2 << '\t' << (float)n/2 << '\t' << static_cast<float>(n)/2 << endl;

1.1.2 int转换成enum

enum Week{
   SUN,MON,TUE,WED,THU,FRI,SAT
};
Week day = 0;

编译上述代码出现如下错误:

g++编译错误:error: invalid conversion from ‘int’ to ‘Week’

把代码Week day = 0;改为Week day = static_cast<Week>(0);可以消除上面的错误。

1.1.3 基类和子类之间指针和引用的转换

已知存在继承关系的两个类Base与Derive。

class Base{
public:
    void Print(){cout << "Base" << endl;}
};
class Derive:public Base{
public:
    void Print(){cout << "Derive" << endl;}
};

1.1.3.1 上行转换

不常用

// 对象
Derive d;
Base b = static_cast<Base>(d);
b.Print();

// 引用
Base& fb = static_cast<Base&>(d);
fb.Print();

// 指针
Base* pb = static_cast<Base*>(new Derive);
pb->Print();

通常使用隐式转换

// 对象
Derive d;
Base b = d;
b.Print();

// 引用
Base& fb = d;
fb.Print();

// 指针
Base* pb = new Derive;
pb->Print();

1.1.3.2 下行转换

这种转换不安全,通常使用dynamic_cast

1.2 const_cast

const_cast主要作用是移除类型的const属性。

用法:
(1)常量指针被转化成非常量的指针,并且仍然指向原来的对象;
(2)常量引用被转换成非常量的引用,并且仍然指向原来的对象;
(3)const_cast一般用于修改底指针。如const char *p形式。

常量赋值给非常量时,会出现下面编译错误:

const int a = 10;

// 指针
const int* cp = &a;
int* p = cp;// error: invalid conversion from ‘const int*’ to ‘int*’

// 引用
const int& cf = a;
int& f = cf;// error: binding ‘const int’ to reference of type ‘int&’ discards qualifiers

通过加入const_cast进行转换可以解决错误

const int a = 10;

// 指针
const int* cp = &a;
int* p = const_cast<int*>(cp);

// 引用
const int& cf = a;
int& f = const_cast<int&>(cf);

注:
1、const_cast<><>中通常只用指针或者引用类型。
2、基本类型常量,因为存在常量展开的情况,const_cast<>并不会改变后面的值。

const int a = 10;
cout << "a:" << a << endl;
const_cast<int&>(a) = 11; // 已经改变a所在内存的值
cout << "a:" << a << endl; // 常量展开,看不到改变
cout << "*(&a)" << *(&a) << endl; // 直接访问内存可以看到改变 
int *p = &a;
cout << "*p:" << *p << endl; // 直接访问内存可以看到改变

通常在const成员函数中是不能修改成员变量。
因为:
(1)在const函数中所有成员变量都是const
(2)在const函数中this指针是const类型,所以所有成员不能被直接改变。

const成员函数中修改成员变量有三种方法
实例:

class Simple{
    mutable int n; //1.通过mutable改
public:
    Simple(int n):n(n){}
    void Test()const{
        n = 3;
        cout << n << endl;
        const_cast<int&>(n) = 4; // 2.通过引用改n
        cout << n << endl;
        const_cast<Simple*>(this)->n = 5; // 3.通过指针改n
        cout << n << endl;
    }
};

方法1
在需要修改的成员变量声明前加上关键字mutable
方法2
const_cast<>()把需要修改的成员变量转成非const类型
方法3
使用const_cast<>()this转换成非const类型,然后修改成员变量。

注:mutable的成员变量可以在const函数中修改。

实例:

#include <iostream>
using namespace std;
enum Week{
    SUN,MON,TUE,WED,TEU,FRI,SAT
};
void Test(int n){
    const_cast<int&>(n) = 11; //用引用改
    cout << "n:" << n << endl;
    const int* p = &n;
    *const_cast<int*>(p) = 12; //用指针改
    cout << "n:" << n << endl;
}
class Simple{
    mutable int n; //1.通过mutable改
public:
    Simple(int n):n(n){}
    void Test()const{
    	n = 3;
	cout << n << endl;
	const_cast<int&>(n) = 4; // 2.通过引用改n
	cout << n << endl;
	const_cast<Simple*>(this)->n = 5; // 3.通过指针改n
	cout << n << endl;
    }    
};
int main(){
    const char* s = "abc";
    cout << s << endl;
    cout << (void*)s << endl;//C强转
    cout << static_cast<const void*>(s) << endl;//C++11强转
    
    Week day = (Week)0;
    Week day2 = static_cast<Week>(0);
    cout << day << " " << day2 << endl;
    
    const int n =10;
    cout << "n:" << n << endl;
    //以下两种方式并没有实际意义上修改,只有通过调用函数申请新的内存才能改动
    const_cast<int&>(n) = 11; //用引用改
    cout << "n:" << n << endl;
    cout << "n:" << *(&n) << endl;
    const int* p = &n;
    *const_cast<int*>(p) = 12; //用指针改
    cout << "n:" << n << endl;
    cout << "n:" << *(&n) << endl;
    Test(n);

    Simple si(1);
    si.Test();
}

abc
0x400cac
0x400cac
0 0
n:10
n:10
n:11
n:10
n:12
n:11
n:12
3
4
5

1.3 dynamic_cast

dynamic_cast是将一个基类对象指针(或引用)转换到继承类指针,dynamic_cast会根据基类指针是否真正指向继承类指针来做相应处理。

用于类的指针、类的引用或者void *转化。
主要有以下三种情况:

上行转换,把子类的指针或引用转换成父类,与static_cast相同。
下行转换,把父类的指针或引用转换成子类,比static_cast安全。
交叉转换,兄弟之间指针转换,static_cast会出现编译错误。

1.3.1 上行转换

不常用

// 对象
Derive d;
Base b = static_cast<Base>(d);
b.Print();

// 引用
Base& fb = static_cast<Base&>(d);
fb.Print();

// 指针
Base* pb = static_cast<Base*>(new Derive);
pb->Print();

1.3.2 下行转换

#include <iostream>
using namespace std;
class Base {
public:
  	void Print() { cout << "Base" << endl; }
  	virtual ~Base(){}
};
class Derive : public Base {
public:
  	void Print() { cout << "Derive" << endl; }
};
int main() { 
    Base * pB = new Derive;
    pB->Print();
    Derive *pD = dynamic_cast<Derive*>(pB);
    pD->Print();
}

1.3.3 交叉转换

实例1:

#include <iostream>

using namespace std;

class Base {
public:
  void Print() { cout << "Base" << endl; }
  virtual ~Base(){}
};
class Derive1 : public Base {
public:
  void Print() { cout << "Derive1" << endl; }
};
class Derive2 : public Base {
public:
  void Print() { cout << "Derive" << endl; }
};

int main() { 
    Derive1* pD1 = new Derive1;
    pD1->Print();
    Derive2 *pD2 = dynamic_cast<Derive2*>(pD1);
    pD2->Print();
}

实例2:

#include <iostream>
using namespace std;
//多态
class Base{
public:
     void Func()const{
        cout << "Base::Func()" << endl;
     }
     virtual ~Base(){}
};
class Derive:public Base{
public:
     void Func()const{
        cout << "Derive::Func()" << endl;
     }
};
int main(){
    Base b;
    b.Func();

    Derive d;
    d.Func();
    d.Derive::Func();

    Base& b2 = d;
    b2.Func(); //调用父类函数,如何调用子类?
    //dynamic_cast<>()
    dynamic_cast<Derive&>(b2).Func(); //父类转子类
}

Base::Func()
Derive::Func()
Derive::Func()
Base::Func()
Derive::Func()

注:dynamic_cast只在多态有效。

1.4 reinterpret_cast

修改了操作数类型,重新解释了给出的对象的比特模型而没有进行二进制转换。

主要用于以下六种情况:
(1)从指针类型到一个足够大的整数类型
(2)从整数类型或者枚举类型到指针类型
(3)从一个指向函数的指针到另一个不同类型的指向函数的指针
(4)从一个指向对象的指针到另一个不同类型的指向对象的指针
(5)从一个指向类函数成员的指针到另一个指向不同类型的函数成员的指针
(6)从一个指向类数据成员的指针到另一个指向不同类型的数据成员的指针

1.4.1 指针类型与整数类型的转化

把指针值(地址)转化成整数,把整数转化成指针值。

#include <iostream>
#include <vector>
using namespace std;
void Func(){
  	cout << "Func" << endl;
}
int main() {
  // 变量指针类型与整数类型转化
  {
    int n = 100;
    int addr = reinterpret_cast<int>(&n);
    cout << "addr:" << hex << addr << dec << " "<< &n << endl;
    int* b = reinterpret_cast<int*>(addr);
    cout << "value:" << *b << endl;
  }
  // 函数指针类型与整数类型转化
  {
    int f = reinterpret_cast<int>(Func);
    typedef void (*pFunc)();
    pFunc pf = reinterpret_cast<pFunc>(f);
    pf();
  }
}

1.4.2 函数指针的转化

FuncNum函数指针转化成FuncAddr,使用FuncAddr的参数列表。

#include <iostream>
#include <vector>

using namespace std;

void FuncNum(int n){
  cout << "num:" << n << endl;
}
void FuncAddr(int* p) { 
  cout << "addr:" << p << endl;
}


int main() {
  int n = 10;
  FuncNum(n);
  FuncAddr(&n);
  
  typedef void (*pfNum)(int n);
  typedef void (*pfAddr)(int* n);
  pfNum pfunc = FuncNum;
  pfunc(n);
  reinterpret_cast<pfAddr>(pfunc)(&n);
}

1.4.3 对象指针的转化

A类对象指针转化成B类的对象指针,使用B类的成员函数。

#include <iostream>
#include <vector>

using namespace std;

class A{
public:
  void Func(){
    cout << "A" << endl;
  }
};

class B {
public:
  void Test() { cout << "B" << endl; }
};

int main() {
  A* pA = new A;
  pA->Func();
  B* pB = reinterpret_cast<B*>(pA);
  pB->Test();
}

1.4.4 类函数成员的转化

A类对象使用B类的成员变量。(A类与B类没有任何关系。)

#include <iostream>
#include <vector>

using namespace std;

class A{
public:
  void Func(){
    cout << "A" << endl;
  }
};

class B {
public:
  void Test() { cout << "B" << endl; }
};

int main() {
  // 对象的函数指针
  {
    A a;
    a.Func();
    typedef void (A::*Func_t)();
    Func_t pfTest = reinterpret_cast<Func_t>(&B::Test);
    (a.*pfTest)();
  }
  // 对象指针的函数指针
  {
    A *pA = new A;
    pA->Func();
    typedef void (A::*Func_t)();
    Func_t pfTest = reinterpret_cast<Func_t>(&B::Test);
    (pA->*pfTest)();
  }
}

1.4.5 类数据成员的转化

#include <iostream>
#include <vector>

using namespace std;

class Test{
public:
  Test(int data) : data(data) {}
private:
  int data;
};
int main() {
  Test test(0x61626364);
  char* p = reinterpret_cast<char*>(&test);
  for (int i = 0; i != sizeof(Test) / sizeof(char); i++) {
    cout << p[i] << endl;
  }
}

1.5 小结

转换 转换对象 作用 转换时机
static_cast 基本类型、指针、引用 实现传统的小括号转化功能 在编译期间实现转换
const_cast const类型的对象、指针、引用 移除变量const限定 在编译期间实现转换
dynamic_cast 类的指针、类的引用或者void * 多态父类指针/引用转化成子类指针/引用 在运行期间实现转换,并可以返回转换成功与否的标志/抛出异常
reinterpret_cast 指针、引用、算术类型 万能强制类型转换 在编译期间实现转换


---------------------
作者:_深蓝.
来源:CSDN
原文:https://blog.csdn.net/qq_42488216/article/details/125217891
版权声明:本文为作者原创文章,转载请附上博文链接!
内容解析By:CSDN,CNBLOG博客文章一键转载插件

猜你喜欢

转载自blog.csdn.net/xiaowang_lj/article/details/132022592