类型转换

1. const_cast

  const_cast是C++中的一个类型转换运算符,用于移除对象的常量性(const)或易变性(volatile)。它可以用于修改指针或引用的常量属性,但必须小心使用,因为修改常量对象的值可能导致未定义的行为。

  const_cast的语法如下:

const_cast<new_type>(expression)

  其中,new_type是要转换为的类型,expression是要转换的表达式。

  几种常见的用法:

  (1) 修改指针的常量属性:

const int* ptr = new int(5);  // 常量指针
int* mutable_ptr = const_cast<int*>(ptr);  //常量指针修改为普通类型指针
*mutable_ptr = 10;  // 修改指针所指向的对象的值

  (2) 修改引用的常量属性:

const int x = 5;  // 常量引用
int& mutable_ref = const_cast<int&>(x);  // 移除常量性
mutable_ref = 10;  // 修改引用所引用的对象的值

  需要注意的是,修改常量对象的值是一种未定义的行为,可能导致程序的不确定性。应该仅在确定安全性的情况下使用const_cast,确保不会对常量对象进行修改。另外,const_cast只能用于移除常量性或易变性,不能用于添加常量性或易变性。

  (3)修改成员函数的常量属性:

常量成员函数的作用域与其他成员函数的作用域相同,它们都属于类的作用域。常量成员函数是类中的特殊函数,它被声明为常量成员函数后,意味着在函数体内不能修改类的数据成员。

class MyClass {
public:
    void myMethod() const {
        // 常量成员函数
    }
};

int main() {
    const MyClass obj;
    const_cast<MyClass&>(obj).myMethod();  // 移除常量性调用常量成员函数
    return 0;
}

  在上面的示例中,const_cast用于移除常量性,以便调用常量成员函数myMethod()。请注意,这样做可能会导致对象的实际状态发生更改,因此请确保在修改之前进行适当的检查。

  (4)修改成员变量的常量属性:

#include <iostream>
using namespace std;
class MyClass {
public:
    int x;
};

int main() {
    const MyClass obj = { 5 };
    obj.x = 10;     //报错 不允许修改
    const_cast<MyClass&>(obj).x = 20;  // 移除常量性修改成员变量的值
    cout << obj.x << endl;    //20
    return 0;
}

  在上面的示例中,const_cast用于移除常量性,以便修改常量对象obj的成员变量x的值。请注意,这样做会改变常量对象的状态,因此要确保在进行修改之前仔细考虑。

 

2. static_cast

static_cast的使用注意事项:
  static_cast可以用于类层次结构中基类和子类之间指针或引用的转换。static_cast仅仅是依靠类型转换语句中提供的信息(尖括号中的类型)来进行转换。

static_cast使用注意事项:

  • static_cast可以用于基本类型的转换,如short与int、int与float、enum与int之间;(编译器隐式执行的任何类型转换static_cast都可以显式完成)。
  • static_cast不能用于在不同类型的指针之间互相转换;
  • static_cast也不能用于整型和指针之间的互相转换;
  • static_cast也不能用于不同类型的引用之间的转换;
  • static_cast也可以用于类类型的转换,但目标类型必须含有相应的构造函数
  • static_cast还可以转换对象的指针类型,但它不进行运行时类型检查(RTTI),所以是不安全的;
  • static_cast甚至可以把任何表达式都转换成void类型;
  • static_cast不能移除变量的const属性,请参考const_cast操作符;
  • static_cast进行的是简单粗暴的转换(仅仅依靠尖括号中的类型),所以其正确性完全由程序员自己保证。
  • static_cast是在编译时进行的,这与dynamic_cast正好相反。

  (1) 基本数据类型转换

double d = 1.2;
int i= static_cast<int> (d); // = C语言的 (int)d;
cout << i << endl;      //1;

  代码举例:

    int c = 10;
    int* a = new int(5);
    char* b = new char('c');
    //b = static_cast<char*>(a);  //报错,类型转换无效 -- 不能用于在不同类型的指针之间互相转换
    //a = static_cast<int*>(c);    //报错,类型转换无效 -- 也不能用于整型和指针之间的互相转换
    int& m = c;
    //char& n = static_cast<char>(&m);    //报错,类型转换无效 -- 也不能用于不同类型的引用之间的转换

   以上说明了static_cast 不能用于在不同类型的指针之间互相转换,也不能用于整型和指针之间的互相转换,当然也不能用于不同类型的引用之间的转换。因为这些属于风险比较高的转换。

  (2) 类层次结构中基类和派生类 之间指针或引用的转换。

  1. 进行上行转换(把派生类的指针或引用转换成基类表示)是安全的;
  2. 进行下行转换(把基类指针或引用转换成派生类表示)时,由于没有动态类型检查,所以是不安全的。因为可能派生类定义了 基类中 没有的成员变量。

  代码举例1:   普通类型使用static_cast转换:

#include <iostream>
#include <typeinfo>
using namespace std;
 
class Base {
public:
    int a;
    void fun1() {cout<<"Base::fun1"<<endl;}
    void fun2() {cout<<"Base::fun2"<<endl;}
};
 
class Derive : public Base{
public:
    int b;
    void fun2() {cout<<"Derive::fun2"<<endl;}
    void fun4() {cout<<"Derive::fun4"<<endl;}
};
 
void test() {
    Base b;
    Derive d;
 
    Base *pB = static_cast<Base*>(&d);    //派生类指针->父类指针
 
    Derive *pD = static_cast<Derive*>(&b); //父类指针->派生类指针
 
    pB->fun1(); // 调用父类的fun1
    pB->fun2(); // 调用父类的fun2
    //pB->fun4(); // 编译错误:error: no member named 'fun4' in 'Base'。 因为fun4是派生类的成员函数,只能通过派生类对象进行访问。
 
    pD->fun1(); //调用父类的fun1
    pD->fun2(); //调用派生类类的fun2
    pD->fun4(); //调用派生类类的fun4,fun4是派生类的成员函数,而不是父类的成员函数。
}

int main()
{
    test();
 
    return 0;
}
/*
编译环境:mac os下用g++编译:
*/

 可见,使用static_cast能够进行派生类和父类的相互转换。

  代码举例2:互不相关的类之间使用static_cast进行转换

#include <iostream>
#include <typeinfo>
using namespace std;
 
class A {
public:
    int a;
    void fun1() {cout<<"A::fun1"<<endl;}
};
 
class B{
public:
    int b;
 
    B(A& a) {cout<<"B::constructor"<<endl;}
 
    void fun2() {cout<<"B::fun2"<<endl;}
};
 
void test() {
    A a;
 
    B b = static_cast<B>(a);    //A->B
 
    //b.fun1(); //编译错误,error: no member named 'fun1' in 'B'; did you mean 'fun2'?。 因为对于b,没有fun1成员函数。
    b.fun2(); //B::fun2
}
    
 
int main()
{
    test();
 
    return 0;
}
/*
编译环境:mac os下用g++编译:
*/

运行结果:

B::constructor
B::fun2

但是,如果在B中,没有定义 B(A& a)的话,就会发生编译错误:

error: no matching conversion for static_cast from 'A' to 'B'
note: candidate constructor (the implicit copy constructor) not viable: no known conversion from 'A' to 'const B' for 1st argument

 可见,如果A和B没有继承关系的两个互不相关的类,想要由A转换为B,则在B中必须定义“以A为参数的”构造函数。

static_cast 和 C 语言 强制转换的差别:

static_cast强制转换会做编译时的类型检查,而C语言的强制转换不会。

代码举例: 两个不相关的类:A,B

 

3.dynamic_cast

dynamic_cast 用于将一个指向基类对象的指针或引用转换为指向派生类对象的指针或引用。它只能用于具有虚函数的类层次结构中,并且在进行类型转换时进行运行时类型检查。

原理dynamic_cast 在进行类型转换时会检查指针或引用指向的实际对象的类型信息(通过运行时类型信息RTTI来实现)。如果转换是可行的,即基类指针或引用指向的对象是目标类型或其派生类的对象,那么 dynamic_cast 返回指向目标类型的指针或引用;否则,如果转换不可行,即基类指针或引用指向的对象不是目标类型或其派生类的对象,那么 dynamic_cast 返回空指针(对于指针转换)或抛出 std::bad_cast 异常(对于引用转换)。

注意事项

虚基类(或称抽象类)可以使用dynamic_cast,但是,非虚基类不可以。

在dynamic_cast被设计之前,C++无法实现从一个虚基类到派生类的强制转换。dynamic_cast就是为解决虚基类到派生类的转换而设计的。

示例代码1: 子类--->父类(虚基类)

#include <stdio.h>
#include <stdlib.h>
//美术家
class Artist {
    public:
        virtual void draw() {printf("Artist draw\n");}
};
 
//音乐家
class Musician {
    public:
};
 
//教师
class Teacher {
    public:
        virtual void teachStu() {printf("Teacher teachStu\n");}
};
 
//即是美术,又是音乐家,又是教师的人
class People: public virtual Artist,public virtual Musician,public Teacher {
    public:
 
};
 
void test1() {
 
    People *p1 = new People();
    p1->draw();
    p1->teachStu();
 
    printf("\ndynamic_cast test:\n");
 
    Artist *a1 =  dynamic_cast<Artist*>(p1);
    //Artist *a1 =  p1; //向上转换,C++总是能够正确识别。即将派生类的指针赋值给基类指针。
 
    a1->draw();     //success: 打印Artist draw
    //a1->teachStu(); //error: no member named 'teachStu' in 'Artist'
 
}
 
int main() {
    test1();
 
    return 0;
 
}

下面两条语句均正确,都能由子类People转化为父类Artist,并且调用父类Artist的成员函数成功。

Artist *a1 =  dynamic_cast<Artist*>(p1); 等价于
Artist *a1 =  p1; 
a1->draw();     //success: 打印people teachStu

可见,向上转换,无论是否用dynamic_cast,C++总是能够正确识别,即将派生类的指针赋值给基类指针

示例代码2:  父类(虚基类)--->子类,采用虚继承的方式:

对于上面的类,再看如下代码:

void test1() {
    People *p1 = new People();
    
    printf("\ndynamic_cast test:\n");
 
    Artist *a1 = p1; //success
    //People *p2 = (People*)a1; //error: cannot cast 'Artist *' to 'People *' via virtual base 'Artist'
    People *p3 = dynamic_cast<People*>(a1);//success:加了dynamic_cast,进行强转
}

分析:

1) //People *p2 = (People*)a1; //error: cannot cast 'Artist *' to 'People *' via virtual base 'Artist',
明确说明,无法直接用(T*)b的形式去进行由父类指针到派生类指针的转换;

2) People *p3 = dynamic_cast<People*>(a1);//success:加了dynamic_cast,进行强转。

由父类指针到派生类指针的转换通过dynamic_cast完成。
示例代码3:  父类(非虚基类)--->子类,采用虚继承的方式:

void test3() {
    People *p1 = new People();
    printf("\ndynamic_cast test:\n");
 
    Musician *m1 = p1; //success
    People *p2 = (People*)m1; //cannot cast 'Musician *' to 'People *' via virtual base 'Musician'
    People *p3 = dynamic_cast<People*>(m1);//error: 'Musician' is not polymorphic
}

分析:

根据dynamic_cast使用注意事项中的“dynamic_cast转换符只能用于含有虚函数的类(虚基类

)”, Musician是非虚基类,所以,无法进行强制dynamic_cast转换。

 

4.reinterpret_cast

  reinterpret_cast 是 C++ 中的一个类型转换操作符,它用于在类型之间进行低级别的重新解释,通常用于将一个类型的对象指针或引用转换为另一个类型的指针或引用。reinterpret_cast 提供了一种将任意类型的指针或引用转换为另一种类型的指针或引用的方法,即使这些类型之间没有直接关系也可以进行转换。

原理:

reinterpret_cast 是一种非常强大但也很危险的类型转换操作符,它主要通过位模式的重新解释来实现转换。它不执行任何转换或调整,而只是对位模式进行重新解释。因此,reinterpret_cast 可能导致类型不匹配或未定义的行为。它应该谨慎使用,只在确保安全性的情况下才能使用。

案例:

  1. 指针类型之间的转换:
int* ptr = new int(10);
char* charPtr = reinterpret_cast<char*>(ptr);

在上述示例中,一个指向 int 类型的指针被重新解释为指向 char 类型的指针。这种转换可能会导致指针访问超出原始类型的范围,因此需要谨慎使用。

  1. 引用类型之间的转换:
int value = 42;
double& doubleRef = reinterpret_cast<double&>(value);

   3. 整型和指针之间的转换

int a = 10;
int* b = reinterpret_cast<int*> (a);

总结: reinterpret_cast 提供了一种低级别的类型转换操作符,允许在不同类型之间进行位模式的重新解释。然而,它的使用需要非常谨慎,因为错误的使用可能会导致类型不匹配、未定义的行为和潜在的安全问题。在使用 reinterpret_cast 时,务必确保对转换结果的合法性进行充分验证,并进行适当的测试。


原文链接:https://blog.csdn.net/liranke/article/details/5295133

     https://blog.csdn.net/qq_45853229/article/details/124721212

        https://blog.csdn.net/liranke/article/details/5145787

posted @ 2023-05-25 00:05  C++杀我  阅读(44)  评论(0编辑  收藏  举报