C++进阶1:xxxx_cast类型转化

1. 类型转换

c语言的转换;
(1)隐式转换(从表示范围小的类型转化为范围大的类型)
(2) 显示类型转化:强制类型转化;

 void*v = p;  //1.1 隐式转换
   p = (int*) v; //1.2显示类型转化;
xxx_cast <类型> (表达式)

一共有以下四种形式: static_cast,const_cast, dynamic_cast,
reinterpret_cast,提供安全性的检测,在代码中更加醒目;

1.1. static_cast

用于非多态类型之间的转换,不提供运行时的检查来确保转换的安全性。
主要有如下

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

1.1.1 基本数据类型转换

  • int转换成char
  char c = 'a';
   cout << c << endl;
   cout << (int)c << endl;
   cout << static_cast<int>(c)<< endl;

1.1.2 int转换成enum

 enum BOOL{
    FALSE,TRUE
};
   BOOL b = static_cast<BOOL>(1==2);

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

g++编译错误:error: invalid conversion from ‘int’ to ‘Week’
clang++编译错误:error: cannot initialize a variable of type ‘const Week’ with an rvalue of type ‘int’
把代码Week day = 0;改为Week day = static_cast(0);可以消除上面的错误。

1.1.3 指针/引用转换

void 指针转换成目标类型的指针,这种转换是不安全的,也需要程序员来保证;
如下代码编译出错,因为不能把 void* 转换成其他具体指针。

 //2.2指针类型之间的转化;(pointer)
   int* arr1 = (int*)malloc(10*sizeof(int));
   int* arr2 = static_cast<int*>(malloc(10*sizeof(float)));
  • 完整案例
#include <iostream>
using namespace std;


enum BOOL{
    FALSE,TRUE
};

int main()
{
   //1 c语言的转换;
   //1.1 隐式转换(从表示范围小的类型转化为范围大的类型)
   //1.2 显示类型转化:强制类型转化;
   int n = 10;
   float f = n;
   n =  f;  //截取

   int* p = &n;
   void*v = p;  //1.1 隐式转换
   p = (int*) v; //1.2显示类型转化;


   //2.1基本类型之间的转化;
   char c = 'a';
   cout << c << endl;
   cout << (int)c << endl;
   cout << static_cast<int>(c)<< endl;
   
   //2.2指针类型之间的转化;(pointer)

   int* arr1 = (int*)malloc(10*sizeof(int));
   int* arr2 = static_cast<int*>(malloc(10*sizeof(float)));
  
  //2.3 bool类型的转化;
   BOOL b = static_cast<BOOL>(1==2);
    cout << b << endl;

    return 0;
}

把Base* pb = pd;改为Base* pb = static_cast<Base*>(pd);,可以解决上述编译错误。

1.2. const_cast

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

  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属性。

  1. 常量指针被转化成非常量的指针,并且仍然指向原来的对象;
  2. 常量引用被转换成非常量的引用,并且仍然指向原来的对象;
  3. const_cast一般用于修改底指针。如const char *p形式。
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.2.1 在const成员函数中修改基本类型

  1. const_cast<>在<>中通常只用指针或者引用类型。
  2. 基本类型常量,因为存在常量展开的情况,const_cast<>并不会改变后面的值。
    (1) 基本类型
    <>里面不能用在基本类型,只能用指针和引用;;
    基础类型onstc常量在使用展开操作,类似于在宏定义的展开(即a的值不变,但是内存的值改变)
 const int a = 10;
   //const_cast<int> a = 11; //(1.1)<>里面不能用在基本类型,只能用指针和引用;
   const_cast<int&>(a) = 12;  //(1.2)引用修改
   cout << &a << ":" << a << endl;  //(1.3)基础类型onstc常量在使用展开操作,类似于在宏定义的展开(即a的值不变,但是内存的值改变啦)

(2)不能修改的字符串对象

   const string s("abcd");
   cout << s << endl;
   const_cast<string&>(s) += "efg";

(3)不能修改的类的成员变量,返回引用和指针类型

 const Simple s1(100);
    cout << s1.Get() << endl;
    const_cast<Simple&>(s1).Set(110);
    cout << s1.Get() << endl;
    const Simple* ps1 = &s1;
    cout << ps1->Get() << endl;
    const_cast<Simple*>(ps1)->Set(120);
    cout << ps1->Get() << endl;
  • 完整案例
#include <iostream>
using namespace std;

class Simple{
    int n;
public:
    Simple(int n):n(n){}
    void Set(int n){ this->n = n;}
    int Get()const { return n;}
};

int main()
{
   //const<>()用来除去常量中的不可改类型;

   //(1)基本类型
   const int a = 10;
   //const_cast<int> a = 11; //(1.1)<>里面不能用在基本类型,只能用指针和引用;
   const_cast<int&>(a) = 12;  //(1.2)引用修改
   cout << &a << ":" << a << endl;  //(1.3)基础类型onstc常量在使用展开操作,类似于在宏定义的展开(即a的值不变,但是内存的值改变啦)




   //(2)不能修改的字符串对象
   const string s("abcd");
   cout << s << endl;
   const_cast<string&>(s) += "efg";
   cout << s << endl;

    //(3)不能修改的类的成员变量,返回引用和指针类型   
    const Simple s1(100);
    cout << s1.Get() << endl;
    const_cast<Simple&>(s1).Set(110);
    cout << s1.Get() << endl;
    const Simple* ps1 = &s1;
    cout << ps1->Get() << endl;
    const_cast<Simple*>(ps1)->Set(120);
    cout << ps1->Get() << endl;

    return 0;
}

1.2.2 在const成员函数中修改成员变量

通常在const成员函数中是不能修改成员变量。

  1. 在const函数中所有成员变量都是const
  2. 在const函数中this指针是const类型,所以所有成员不能被直接改变。
  3. mutable的成员变量可以在const函数中修改

(1)const函数中所有的成员变量都是const,在const函数中,this指针是const类型,所以所有成员不能改变;
方法1:把需要修改的成员变量const_cast<>()改成非const类型;

++const_cast<int&>(getter);  //方法1:把需要修改的成员变量const_cast<>()改成非const类型;

方法2: 把this指针修改为需const_cast<>()改成非const类型;但是只能在本次生效;

++(const_cast<Integer*>(this)->getter);  //方法2:把this指针修改为需const_cast<>()改成非const类型;但是只能在本次生效;

方法3:mutable的成员变量在const函数中修改

 mutable int getter; //3.mutable的成员变量在const函数中修改

关键字mutable的调用

++getter;  //3.2关键字mutable的调用
  • 完整案例
#include <iostream>
using namespace std;

class Integer{
private:
    int n;
    int setter;
    mutable int getter; //3.mutable的成员变量在const函数中修改
public:
    Integer(int n):n(n),setter(0),getter(0){}
    void Set(int n){
    ++setter;
    this->n=n;
    }
    int Get()const{  //(1)const函数中所有的成员变量都是const,在const函数中,this指针是const类型,所以所有成员不能改变;
    //++const_cast<int&>(getter);  //方法1:把需要修改的成员变量const_cast<>()改成非const类型;
    //++(const_cast<Integer*>(this)->getter);  //方法2:把this指针修改为需const_cast<>()改成非const类型;但是只能在本次生效;
    ++getter;  //3.2关键字mutable的调用
    return n;
    }

    void PrintCount()const{
    cout << "set:" << setter <<"\t" << "get:" <<getter<<endl;
    }
};

int main()
{
    Integer n(10);
    n.Set(2);
    cout << n.Get() << endl;
    n.Set(4);
    cout << n.Get() << endl;
    n.PrintCount();
    return 0;
}

1.3. dynamic_cast

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

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

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

1.3.1.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.3.1.2 下行转换

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

用于类的指针、类的引用或者void *转化。

主要用于以下三种情况:

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

如果时指针,进行正确的转换,获得对应的值;否则返回NULL,如果是引用,则在运行时就会抛出异常;

注意: (1)下行转化;把父类指针转化为子类指针
父类指针必须指向当前类型的子类对象;

 Cat* cat = dynamic_cast<Cat*>(arr[i]);
        //下行转化;把父类指针转化为子类指针
        //(1)父类指针必须指向当前类型的子类对象;

(2)转化的函数必须要有虚函数;

virtual string Feature()const {return "";} ;//(2)转化的函数必须要有虚函数;
  • 完整案例
#include <iostream>
#include  <cstring>
using namespace std;


class Animal{
private:
    string name;
public:
    Animal(const string& name):name(name){}
    virtual string Feature()const {return "";} ;//(2)转化的函数必须要有虚函数;
    const string& GetName()const{return name;}
};

class Cat:public Animal{
private:
    int n;
public:
    Cat(const string& name,int n):Animal(name),n(n){}
    string  Feature()const{
    return "抓老鼠";
    }
    int GetMouseNum()const{
        return n;
    }

};

class Dog:public Animal{
public:
    Dog(const string& name):Animal(name){}
    string Feature()const{
    return "看门";
    }

};

void Display(Animal* arr[],int n){
    for(int i =0; i<n;++i){
        cout << arr[i]->GetName() << arr[i]->Feature() ;
        Cat* cat = dynamic_cast<Cat*>(arr[i]);
        //下行转化;把父类指针转化为子类指针
        //(1)父类指针必须指向当前类型的子类对象;


        if(NULL != cat){
            cout << "抓了" << cat->GetMouseNum() <<"只老鼠";

        }
        cout << endl;
    }

}
int main()
{
    Animal* arr[] = {new Cat("猫",5),new Dog("狗")};
    Display(arr,2);
    return 0;
}

1.3.1.3 交叉转换

#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();
}

dynamic_cast只在多态有效。

1.3.1.4扩展问题

  • 如果不是多态会有什么情况?== 编译错误==
  • dynamic_cast为什么只在多态的继承关系才有效?== 由于运行时类型检查需要运行时类型信息,而这个信息存储在类的虚函数表。==

1.4. reinterpret_cast

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

主要用于以下六种情况:

  1. 从指针类型到一个足够大的整数类型
  2. 从整数类型或者枚举类型到指针类型
  3. 从一个指向函数的指针到另一个不同类型的指向函数的指针
  4. 从一个指向对象的指针到另一个不同类型的指向对象的指针
  5. 从一个指向类函数成员的指针到另一个指向不同类型的函数成员的指针
  6. 从一个指向类数据成员的指针到另一个指向不同类型的数据成员的指针

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

把指针值(地址)转化成整数,把整数转化成指针值。
(1)将数值转化为对应的指针类型;

   void* p = reinterpret_cast<void*>(n);
    cout << p << endl;

(2)将指针转化W对应的数值类型
(2.1)注意:精度不能缺失;

   long addr = reinterpret_cast<long>(p);
   cout << addr << endl;

1.4.2 函数指针的转化

(1)&进行取地址,可以没有,指针转化1

void* pf = reinterpret_cast<void*>(&Func);  //(3.1)&进行取地址,可以没有,指针转化1

(2) 指针转化2:注意类型的对应;后面的void(*) 要注意;

 void (*func)() = reinterpret_cast<void(*)()>(pf);  //(3.2)指针转化2:注意类型的对应;后面的void(*) 要注意;
    func();
  • 完整案例
#include <iostream>
using namespace std;

void Func(){
    cout << __func__<<endl;
    cout << __cplusplus <<endl;
}

int main()
{
    int n = 100;

    //(1)将数值转化为对应的指针类型;
    void* p = reinterpret_cast<void*>(n);
    cout << p << endl;

    //(2)将指针转化W对应的数值类型
    //(2.1)注意:精度不能缺失;

    long addr = reinterpret_cast<long>(p);
    cout << addr << endl;

    void* pf = reinterpret_cast<void*>(&Func);  //(3.1)&进行取地址,可以没有,指针转化1
    void (*func)() = reinterpret_cast<void(*)()>(pf);  //(3.2)指针转化2:注意类型的对应;后面的void(*) 要注意;
    func();
    return 0;
}

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;
  }
}

谨慎使用reinterpret_cast

2. 小结

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

3.cout打印地址

3.1 基本类型:如果要打印地址需要强制转换

 #include <iostream>
    using namespace std;
    void f(){}
    
    int main(){
        int n = 10;
        int* p = &n;
        const char* s = "abc";
        cout << p << " " << (void*) p << endl;
        cout << s << " " << (void*) s << endl;
        cout << f << " " << (void*) f << endl;
    }

试着把上面改成C++的转换方式

(1)普通类型的指针直接打印指针名即可打印地址;

int n = 10;
int *p = &n;
cout << p << endl;

(2)打印字符串数组地址使用 时 static_cast<>()

 const char* s = "Hello World";
    cout << s << " " <<static_cast<const void*>(s) << endl;

(3)函数地址打印需要用reinterpret<>()转化

cout <<reinterpret_cast<void*>(&Func)<< endl;
cout << (void*)(&Func) << endl;
  • 完整案例
#include <iostream>
using namespace std;

void Func(){}

int main()
{
    //(1)普通类型的指针直接打印指针名即可打印地址;
    int n = 10;
    int *p = &n;
    cout << p << endl;
    
    int arr[] = {1,2};
    cout << arr << endl;
    
    //(2)打印字符串数组地址使用  tatic_cast<>()
    const char* s = "Hello World";
    cout << s << " " <<static_cast<const void*>(s) << endl;


    //(3)函数地址打印需要用reinterpret<>()转化
    cout <<reinterpret_cast<void*>(&Func)<< endl;
    cout << (void*)(&Func) << endl;

    return 0;
}

3.2 类:如果要打印地址(成员变量+成员函数)

  1. 成员变量在对象中的偏移量,且只能用printf()打印,而且类型放在前面;

1.1 &类名::成员变量 获取成员变量在对象中的偏移量;(%d)
(%p—地址)

 printf("%d %d %d\n",&Point3D::x,&Point3D::y,&Point3D::z);
    printf("%p\n",&Point); //1.2 (%p---地址)
    printf("%p %p %p\n", &Point.x,&Point.y,&Point.z);//成员变量的地址;

1.2 cout 只能打印出 1 1 1(类似于函数)

 cout << &Point3D::x << &Point3D::y<<&Point3D::z << endl;  //只能打印出 1 1 1(类似于函数)

2.&对象。成员变量 获取成员变量的地址

cout << (void*)(&Point3D::print) << endl;
cout << reinterpret_cast<void*>(&Point3D::print) << endl;  //成员函数获取地址前面必须加上&
  • 完整案例
#include <iostream>
using namespace std;

class  Point3D{
public:
    int x,y,z;
    Point3D(int x, int y, int z):x(x),y(y),z(z){}
    void print()const{
    cout << "(" << x << "," << y << "," << z << ")" << endl;
    }

};


int main()
{
    Point3D Point(1,2,3);
    Point.print();

    //1.成员变量在对象中的偏移量,且只能用printf()打印,而且类型放在前面;
    //1.1 &类名::成员变量 获取成员变量在对象中的偏移量;(%d)
    printf("%d %d %d\n",&Point3D::x,&Point3D::y,&Point3D::z);
    printf("%p\n",&Point); //1.2 (%p---地址)
    printf("%p %p %p\n", &Point.x,&Point.y,&Point.z);//成员变量的地址;
    
    cout << &Point3D::x << &Point3D::y<<&Point3D::z << endl;  //只能打印出 1 1 1(类似于函数)

    //2.&对象。成员变量  获取成员变量的地址
    cout << (void*)(&Point3D::print) << endl;
    cout << reinterpret_cast<void*>(&Point3D::print) << endl;  //成员函数获取地址前面必须加上&
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值