掘根宝典之C++RTTI和类型转换运算符(dynamic_cast,const_cast,static_cast,reinterpret_cast)

什么是RTTI

RTTI是运行阶段类型识别的简称。

哪些是RTTI?

C++有3个支持RTTI的元素。

1.dynamic_cast运算符将使用一个指向基类的指针来生成一个指向派生类的指针,否则该运算符返回0——空指针。

2.typeid运算符返回一个指出对象类型的信息

3.type_info结构存储了特定类型的信息

注意:只能将RTTI用于包含虚函数的类层次结构,原因在于只有对于这种类层次,才应该将派生类对象的地址赋给基类指针。

警告:RTTI只适用于包含虚函数的类。

dynamic_cast运算符

在C++中,dynamic_cast是一种运行时类型检查和转换的操作符,用于在继承体系中进行安全的向下转型。它的语法如下:

dynamic_cast<目标类型>(表达式)

其中,目标类型是你想要将表达式转换为的类型,表达式是需要转换的对象。

dynamic_cast的使用有以下几个要点:

  1. dynamic_cast只能在具有多态性(即包含虚函数的类)的类之间进行类型转换。(is-a关系里可用)

  2. dynamic_cast会在运行时检查目标类型是否与表达式的类型兼容,如果不兼容则返回nullptr(对于指针类型)或抛出std::bad_cast异常(对于引用类型)。因此,在使用dynamic_cast时应该先判断转换结果是否为nullptr(空指针)或捕获异常。

  3. dynamic_cast只能用于指针或引用类型的转换。对于指针类型,如果转换失败,则返回nullptr;对于引用类型,如果转换失败,则抛出std::bad_cast异常。

下面是一个使用dynamic_cast的示例:

#include
using namespace std;
class Base {
public:
    virtual void foo() {
        cout << "Base::foo()" << endl;
    }
};

class Derived : public Base {
public:
    void foo() {
        cout << "Derived::foo()" << endl;
    }
};

int main() {
    //用基类指针创建派生类对象
    Base* basePtr = new Derived();
    //将基类指针强制转换为派生类对象
    Derived* derivedPtr = dynamic_cast(basePtr);
    //判断是否转换成功
    if (derivedPtr!=nullptr) {
        derivedPtr->foo();  // 输出 "Derived::foo()"
    }
    else {
        cout << "dynamic_cast failed!" << endl;
    }

    delete basePtr;  // 记得释放内存

    return 0;
}

运行结果

Derived::foo()

 在上面的示例中,由于Derived是Base的派生类,我们可以将Derived类型的对象的地址赋给Base指针。然后,通过使用dynamic_cast将Base指针转换为Derived指针,我们可以调用Derived类的成员函数。

我们看个反面例子

#include
using namespace std;
class Base {
public:
    virtual void foo() {
        cout << "Base::foo()" << endl;
    }
};

class Derived : public Base {
public:
    void foo() {
        cout << "Derived::foo()" << endl;
    }
};

int main() {
    //用基类指针创建基类对象
    Base* basePtr = new Base();
    //将基类指针强制转换为派生类指针
    Derived* derivedPtr = dynamic_cast(basePtr);
    //判断是否转换成功
    if (derivedPtr!=nullptr) {
        derivedPtr->foo();  // 输出 "Derived::foo()"
    }
    else {
        cout << "dynamic_cast failed!" << endl;
    }

    delete basePtr;  // 记得释放内存

    return 0;
}

 运行结果

dynamic_cast failed

 在上面的示例中,由于Derived是Base的派生类,发现不能安全的将指向基类地址的指针赋给派生类指针,所以derivedPtr被dynamic_cast赋予了空指针。

需要注意的是,如果Base类中没有声明为虚函数的成员函数,那么dynamic_cast无法进行类型转换。在这种情况下,可以考虑使用static_cast(我们等会会讲)进行类型转换,但这样做是不安全的,因为static_cast不会进行运行时类型检查。

typeid运算符和type_info类

在C++中,typeid运算符用于获取表达式的类型信息。它的语法如下:

typeid(表达式)

其中,表达式可以是任意类型的对象、类名、变量或者表达式。

注意包含头文件

typeid运算符的使用有以下几个要点:

  1. typeid运算符返回一个std::type_info对象,它包含了类型的信息,可以用于比较两个类型是否相同。

  2. typeid运算符可以用于动态类型识别,即在运行时判断对象的实际类型。

  3. typeid运算符只能用于具有多态性(即包含虚函数的类)的类。

  4. typeid运算符对指针和引用类型的表达式进行操作时,返回的是指针或引用所指向的对象的类型信息。

下面是一个使用typeid的示例:

#include 
#include 

class Base {
public:
    virtual void foo() {}
};

class Derived : public Base {};

int main() {
    Base* basePtr = new Derived();

    std::cout << typeid(*basePtr).name() << std::endl;  // 输出 "class Derived"

    Base& baseRef = *basePtr;

    std::cout << typeid(baseRef).name() << std::endl;  // 输出 "class Derived"

    delete basePtr;  // 记得释放内存

    return 0;
}

运行结果

class Derived
class Derived

在上面的示例中,我们创建了一个Base指针指向Derived对象,并通过typeid运算符获取了该对象的类型信息。可以看到,typeid运算符返回的是一个std::type_info对象,使用其name()方法可以获取类型的名称。

需要注意的是,typeid运算符返回的类型名称可能是编译器特定的名称,所以并不是在所有编译器中都能得到相同的结果。此外,typeid运算符还可以用于比较两个类型是否相同(使用==运算符进行比较),以及在异常处理中判断对象的类型。

typeid(a)==typeid(b)

判断a,b的类型是否相同

类型转换运算符

我们先看看之前的强制转换

int a=(int)3.0;
char b=char(888.0);

这些强制转换真的安全吗?

肯定不安全啊

为此,C++引入了4个类型转换运算符:dynamic_cast,const_cast,static_cast,reinterpret_cast

dynamic_cast

我们在上面已经讲了这个的用法,这个运算符的用途是能使得在类结构层次中进行向上转换(只能在is-a关系),而不允许其他转换

什么?你不知道is-a关系?看这里http://t.csdnimg.cn/JUgEI

const_cast

在C++中,const_cast运算符用于去除指针或引用的const或volatile修饰符。它可以用于将const指针或引用转换为非const指针或引用,或者将volatile指针或引用转换为非volatile指针或引用。

const_cast的语法如下:

const_cast(expression)

其中,type表示要转换的类型,expression表示要转换的表达式。

使用const_cast需要注意以下几点:

  1. const_cast只能用于去除const或volatile修饰符,不能用于增加修饰符。

  2. const_cast只能用于指针或引用类型,不能用于其他类型。

  3. 对于指针类型,const_cast只能改变指针的底层const,不能改变指针所指向的对象的const或volatile属性。

下面是一些使用const_cast的例子:

const int num = 10;
int* ptr = const_cast(&num);  // 将const int*转换为int*
*ptr = 20;  // 修改了num的值

const int* const_ptr = #
int* non_const_ptr = const_cast(const_ptr);  // 将const int*转换为int*
*non_const_ptr = 30;  // 修改了num的值

volatile int val = 100;
const int* volatile_ptr = const_cast(&val);
*volatile_ptr = 200;  // 错误,不能修改volatile变量的值

在上面的例子中,我们分别使用了const_cast将const int和const int volatile转换为int*。注意最后一个例子中的错误使用,const_cast无法将volatile变量转换为非volatile类型。

static_cast

这个可能是我们最常用的类型转换运算符

在C++中,static_cast是一种用于执行静态类型转换的运算符。它可以用于将一个类型转换为另一个相关的类型,如将整数类型转换为浮点类型,指针类型与整数类型之间的转换等

static_cast的语法如下:

static_cast(expression)

其中,type表示要转换的目标类型,expression表示要转换的表达式。

使用static_cast需要注意以下几点:

  1. static_cast可以用于常见的隐式类型转换如整数类型之间的转换、浮点类型之间的转换等。

  2. static_cast可以用于具有继承关系的指针或引用类型之间的转换,但只能在相互兼容的类型之间进行转换。

  3. static_cast不能用于去除const或volatile修饰符,如果需要去除const或volatile修饰符,应该使用const_cast或volatile_cast。

下面是一些使用static_cast的例子:

int num = 10;
double d = static_cast(num);  // 将int转换为double

int a = 100;
void* void_ptr = static_cast(&a);  // 将int*转换为void*

class Base {};
class Derived : public Base {};
Base* base_ptr = new Derived();
Derived* derived_ptr = static_cast(base_ptr);  // 将Base*转换为Derived*

在上面的例子中,我们分别使用了static_cast将整数类型、指针类型和引用类型进行了转换。注意最后一个例子中的指针类型转换,它只能在具有继承关系的类型之间进行转换,并且只能在相互兼容的类型之间进行转换。

reinterpret_cast

这个可能是最不常使用的类型转换运算符

在C++中,reinterpret_cast是一种用于执行重新解释类型的运算符。它可以将一种类型的指针或引用转换为另一种类型的指针或引用,甚至是不相关的类型。

reinterpret_cast的语法如下:

reinterpret_cast(expression)

其中,type表示要转换的目标类型,expression表示要转换的表达式。

使用reinterpret_cast需要注意以下几点:

  1. reinterpret_cast可以执行任意类型之间的转换即使类型之间没有任何关系。

  2. reinterpret_cast不进行类型检查或安全检查,因此在使用时需要非常小心。

  3. reinterpret_cast通常用于需要底层二进制表示或将指针转换为整数类型的场景。

下面是一些使用reinterpret_cast的例子:

int num = 42;
char* char_ptr = reinterpret_cast(&num);  // 将int*转换为char*

int* num_ptr = reinterpret_cast(char_ptr);  // 将char*转换为int*

int i = 10;
int* iptr = reinterpret_cast(&i);  // 将int转换为int*

class A {};
class B {};
A* a_ptr = new A();
B* b_ptr = reinterpret_cast(a_ptr);  // 将A*转换为B*

上面的例子中,我们分别使用了reinterpret_cast将不同类型的指针进行了转换。注意在第一个例子中,我们将int指针转换为char指针,并且reinterpret_cast没有进行任何类型检查,因此需要非常小心使用。

你可能感兴趣的:(c++,c++,开发语言)