详解c++中的类型识别

 更新时间:2020年06月30日 09:17:37   作者:PRO_Z  
这篇文章主要介绍了 详解c++中的类型识别,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

1、类型识别的相关概念

(1)类型识别的作用

  类型识别是面向对象中引入的一个新概念,主要用来判断赋值兼容性原则中的类型问题,即此时的数据类型到底是基类类型还是派生类类型?

  当基类指针指向子类对象 或者基类引用成为子类对象的别名 时,就需要使用类型识别;

Base *p = new Derived();
Base &r = *p

对于上面的语句,我们可以这样认识,指针p是Base类型,但是P 又指向了一个新的Derived类型,此时很难判断指针P 的数据类型;同理,引用r 本来作为父类的别名而存在,但由于赋值兼容性,引用r也可以作为子类的别名,同样此时 引用 r 的数据类型也不能确定;

  注:1)由之前所学知识,若没有虚函数重写,编译器为了安全起见,会将指针p 当作 Base 类型;(编译期间)    

    2)若有虚函数重写,就会发生动态多态特性,此时就会根据指针p 所指向的具体数据类型来确定指针p 的数据类型。(运行期间)

(2)类型识别的分类

  1)静态类型:变量(对象)自身的类型;在编译阶段就能确定所使用变量的数据类型。

  2)动态类型:指针(引用)所指向对象的实际类型;在运行阶段根据指针所指向的具体数据类型来确定所使用的数据类型。

    Base *b 所指向的实际对象无法确定,若指针b 指向的是子类对象,则程序正常运行;若指针b 指向的是父类对象,则程序有可能出现 Bug;

    注:在 g++ 编译器下上述情况均可正常运行,但后者不建议使用;

  在赋值兼容原则中,基类指针是否可以强制类型转换为子类指针取决于动态类型;(很重要!!!)--- 只有动态类型是子类对象才能进行合法转换

2、如何得到动态类型

(1)利用多态

  1)必须从基类开始提供类型虚函数;

  2)所有的派生类都必须重写类型虚函数;

  3)每个派生类的类型 ID必须唯一;

   结果:调用类型虚函数就可以知道当前的对象究竟是什么类型,这样就可以得到动态类型,达到动态类型识别效果;

利用类型虚函数实现类型识别

 #include <iostream>
 #include <string>
 
 using namespace std;
 
 class Base
 {
 public:
   enum { ID = 0 };
   
   virtual int type() // 类型虚函数
   {
     return ID;
   }
 };
 
 class Derived : public Base
 {
 public:
   enum { ID = 1 };
   
   int type()
   {
     return ID;
   }
   
   void print()
   {
     cout << "I'm a Derived. " << endl;
   }
 };
 
 class Child : public Base
 {
 public:
   enum { ID = 2 };
   
   int type()
   {
     return ID;
   }
 };
 
 void test(Base* pb)
 {
   if( pb->type() == Child::ID )
   {
     Child* pc = static_cast<Child*>(pb);
     //Child* pc = dynamic_cast<Child*>(pb);  // 同上
     
     cout << "& = " << pc << endl;
     cout << "I'm a Child. " << endl;
   }
   
   if( pb->type() == Derived::ID )
   {
     Derived* pd = static_cast<Derived*>(pb);
     //Derived* pd = dynamic_cast<Derived*>(pb); // 同上
     
     cout << "& = " << pd << endl;
     pd->print();
   }
   
   if( pb->type() == Base::ID )
   {
     cout << "& = " << pb << endl;
     cout << "I'm a Base. " << endl;
   }
 }
 
 int main(int argc, char *argv[])
 {
   Base b;
   Derived d;
   Child c;
   
   test(&b);
   test(&d);
   test(&c);
   
   return 0;
 }
 /**
 * 运行结果:
 * & = 0x7ffccf0dd850
 * I'm a Base. 
 * & = 0x7ffccf0dd860
 * I'm a Derived.
 * & = 0x7ffccf0dd870
 * I'm a Child.
 */

(2)利用 dynamic_cast

  1)dynamic_cast这个关键字如果要转换的实际类型和指定的类型不一样,则会返回NULL。例如当指定类型为子类对象时,如果父类指针的动态类型是这个子类对象时,转换成功,而动态类型是父类对象或者其他子类对象时,转换失败;

  2)dynamic_cast 要求使用的目标对象类型必须是多态,即:所在类族至少有一个虚函数;

  3)只能用于指针和引用之间的转换

    1.用于指针转换时,转换失败,返回空指针;

    2.用于引用转换时,转换失败,将引发 bad_cast异常。

 #include <iostream>
 #include <string>
 
 using namespace std;
 
 class Base
 {
 public:
   virtual ~Base()
   {
   
   }
 };
 
 class Derived : public Base
 {
 public:  
   void print()
   {
     cout << "I'm a Derived. " << endl;
   }
 };
 
 class Child : public Base
 {
 
 };
 
 void test(Base* pb)
 {
   // dynamic_cast 只能确定最终的转化结果,无法获取动态类型的原型
   Derived* pd = dynamic_cast<Derived*>(pb);
   
   if(pd != NULL)
   {
     // Derived 类类型, 可以使用指针pd访问Derived类的成员
     cout << "& = " << pd << endl;
     pd->print();
   }
   else
   {
     Child* pc = dynamic_cast<Child*>(pb);
     
     if(pc != NULL)
     {
       // Child 类类型, 可以使用指针pc访问Child类的成员
       cout << "& = " << pc << endl;
       cout << "I'm a Child. " << endl;
     }
     else
     {
       // Base 类类型, 可以使用指针pb访问Base类的成员
       cout << "& = " << pc << endl; 
       cout << "I'm a Base. " << endl;
     }
   }
 }
 
 int main(int argc, char *argv[])
 {
   Base b;
   Derived d;
   Child c;
   
   test(&b);
   test(&d);
   test(&c);
   
   return 0;
 }
 /**
 * 运行结果:
 * & = 0
 * I'm a Base. 
 * & = 0x7ffccf0dd860
 * I'm a Derived.
 * & = 0x7ffccf0dd870
 * I'm a Child.
 */

(3)利用 typeid(推荐这种方法)

  1)typeid是一个关键字,专门用于动态类型识别;

  2)typeid 关键字返回对应参数的类型信息,此类型信息是一个type_info类对象;

    1.当参数为类型时,返回静态类型信息;

    2.当参数为变量时:1> 参数变量内部不存在虚函数表时,返回静态类型信息; 2> 参数变量内部存在虚函数表时,返回动态类型信息;

    3.当参数为 NULL 时,将抛出异常;

  3)typeid使用时需要包含头文件<typeinfo>;

  4)typeid 使用时直接指定对象或者类型。

  5)typeid 在不同的编译器内部实现是不同的;

int i = 0;

const type_info& tiv = typeid(i); // 将 i 的类型信息放到 type_info 中去;
const type_info& tii = typeid(int);

cout << (tiv == tii) << endl; // 1

利用 typeid 实现类型识别

 #include <iostream>
  #include <string>
  #include <typeinfo>
  
  using namespace std;
  
  class Base
  {
  public:
   virtual ~Base()
   {
   }
 };
  
 class Derived : public Base
 {
 public:
   void print()
   {
     cout << "I'm a Derived." << endl;
   }
 };
  
 class Child : public Base 
 {
 public:
   void print()
   {
     cout << "I'm a Child." << endl;
   }
 };
  
 void test(Base* pb)
 {
   const type_info& tb = typeid(*pb);
   
   if( tb == typeid(Derived) )
   {
     Derived* pd = dynamic_cast<Derived*>(pb);
   
     cout << "& = " << pd << endl;
     pd->print();
   }
   else if( tb == typeid(Child) )
   {
     Child* pc = dynamic_cast<Child*>(pb);
     
     cout << "& = " << pc << endl;
     pc->print();
     
   }
   else if( tb == typeid(Base) )
   {
     cout << "& = " << pb << endl;
     cout << "I'm a Base. " << endl;
   }
   
   cout << tb.name() << endl;
 }
  
 int main(int argc, char *argv[])
 {
   Base b;
   Derived d;
   Child c;
   int index;
   char ch;
   
   const type_info& tp = typeid(b);
   const type_info& tc = typeid(d);
   const type_info& tn = typeid(c);
   const type_info& ti = typeid(index);
   const type_info& tch = typeid(ch);
   
   cout<<tp.name()<<endl;
   cout<<tc.name()<<endl;
   cout<<tn.name()<<endl;
   cout<<ti.name()<<endl;
   cout<<tch.name()<<endl;
   
   test(&b);
   test(&d);
   test(&c);
   
   return 0;
 }
 /**
  * 运行结果:
  * 4Base
  * 7Derived
  * 5Child
  * i
  * c
  * & = 0x7ffcbd4d6280
  * I'm a Base. 
  * 4Base
  * & = 0x7ffcbd4d6290
  * I'm a Derived.
  * 7Derived
 * & = 0x7ffcbd4d62a0
 * I'm a Child.
 * 5Child
 */ 

  结论:

  3 种动态类型的实现方法 建议选 第3种 (typeid)。

  对于多态实现,存在以下缺陷:

    1)必须从基类开始提供类型虚函数;

  2)所有的派生类都必须重写类型虚函数;

  3)每个派生类的类型名必须唯一;

  对于 dynamic_cast 实现,只能得到类型转换的结果,不能获取真正的动态类型,同时dynamic_cast 必须多态实现。

到此这篇关于 详解c++中的类型识别的文章就介绍到这了,更多相关c++ 类型识别内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • c语言 树的基础知识(必看篇)

    c语言 树的基础知识(必看篇)

    下面小编就为大家带来一篇c语言 树的基础知识(必看篇)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-05-05
  • C语言中字符串库函数的实现及模拟

    C语言中字符串库函数的实现及模拟

    C语言中有很多数据类型,比如int(整数类型)、char(字符类型)、以及浮点型的double(双精度)等。但是有一点就是我们发现这里并没有提到我们常见的有关字符串的类型。本文为大家介绍了C语言中字符串库函数的实现及模拟,需要的可以参考一下
    2022-11-11
  • C++存储持续性生命周期原理解析

    C++存储持续性生命周期原理解析

    这篇文章主要为大家介绍了C++存储持续性生命周期原理解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-01-01
  • C++单例模式的实例详解

    C++单例模式的实例详解

    这篇文章主要介绍了C++单例模式的实例详解的相关资料,希望通过本文能帮助到大家,让大家掌握这部分内容,需要的朋友可以参考下
    2017-10-10
  • 详解C++设计模式编程中建造者模式的实现

    详解C++设计模式编程中建造者模式的实现

    这篇文章主要介绍了C++设计模式编程中建造者模式的实现,建造者模式将一个复杂对象的构建于它的表现分离,可以减少代码冗余,需要的朋友可以参考下
    2016-03-03
  • C语言中文件处理全攻略详解

    C语言中文件处理全攻略详解

    这篇文章主要为大家详细介绍了C语言中文件处理的相关知识,包括创建、写入、追加操作解析,文中的示例代码讲解详细,感兴趣的小伙伴可以了解一下
    2024-01-01
  • 一起来学习C语言的字符串转换函数

    一起来学习C语言的字符串转换函数

    这篇文章主要为大家详细介绍了C语言的字符串转换函数,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助
    2022-02-02
  • C语言学习之柔性数组详解

    C语言学习之柔性数组详解

    结构体的最后一个元素允许是未知大小的数组,这就叫柔性数组。这篇文中主要为大家详细介绍了C语言中柔性数组的相关知识,需要的可以了解一下
    2023-03-03
  • 关于C++的重载运算符和重载函数

    关于C++的重载运算符和重载函数

    一般来说,重载运算符在实际的项目开发中会经常的用到,但如果某些自定义类型通过简短几行代码重载一些常用的运算符(如:+-*/),就能让编程工作带来方便,需要的朋友可以参考下本文
    2023-05-05
  • 详解C语言的预处理效果

    详解C语言的预处理效果

    这篇文章主要为大家介绍了C语言的预处理效果,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助
    2021-12-12

最新评论