详解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++ 类型识别内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • C++中结构体的类型定义和初始化以及变量引用

    C++结构体类型的定义和初始化 有时需要将不同类型的数据组合成一个有机的整体,以供用户方便地使用.这些组合在一个整体中的数据是互相联系的.例如,一个学生的学号.姓名.性别.年龄.成绩.家庭地址等项,都是这个学生的属性,见图 可以看到学号(num).姓名(name).性别(sex).年龄(age).成绩(score ).地址(addr)是与姓名为"Li Fun"的学生有关的.如果在程序中将num,name,sex,age,score,addr分别定义为互相独立的变量,就难以反映出它们之间

  • 深入解析C++中的引用类型

    c++比起c来除了多了类类型外还多出一种类型:引用.这个东西变量不象变量,指针不象指针,我以前对它不太懂,看程序时碰到引用都稀里糊涂蒙过去.最近把引用好好地揣摩了一番,小有收获,特公之于社区,让初学者们共享. 引用指的是对一个对象的引用.那么什么是对象?在c++中狭义的对象指的是用类,结构,联合等复杂数据类型来声明的变量,如 MyClass myclass,CDialog  mydlg,等等.广义的对象还包括用int,char,float等简单类型声明的变量,如int a,char b等等.我在

  • C++中将string类型转化为int类型

    写程序需要将string转化为int,所以就探索了一下. 方法一:atoi函数 atoi函数将字符串转化为整数,注意需要stdlib库.所以就尝试了一下: #include <iostream> #include <string.h> #include <stdlib.h> using namespace std; int main() { string a="11",b="22"; cout<<atoi(a)+ato

  • 用标准c++实现string与各种类型之间的转换

    要实现这个目标,非stringstream类莫属. 这个类在头文件中定义, < sstream>库定义了三种类:istringstream.ostringstream和stringstream,分别用来进行流的输入.输出和输入输出操作.另外,每个类都有一个对应的宽字符集版本. 简单起见,我主要以stringstream为中心,因为每个转换都要涉及到输入和输出操作. 示例1示范怎样使用一个stringstream对象进行从 string到int类型的转换 注意,使用string对象来代替字符数组

  • C和C++中的基本数据类型的大小及表示范围详解

    本文研究的主要问题时关于C和C++中的基本数据类型int.long.long long.float.double.char.string的大小及表示范围,具体介绍如下. 一.基本类型的大小及范围的总结(以下所讲都是默认在32位操作系统下): 字节:byte:位:bit. 1.短整型short:所占内存大小:2byte=16bit: 所能表示范围:-32768~32767:(即-2^15~2^15-1) 2.整型int:所占内存大小:4byte=32bit: 所能表示范围:-2147483648~

  • C/C++ ip地址与int类型的转换实例详解

    C/C++ ip地址与int类型的转换实例详解 前言 最近看道一个面试题目,大体意思就是将ip地址,例如"192.168.1.116"转换成int类型,同时还能在转换回去 思路 ip地址转int类型,例如ip为"192.168.1.116",相当于"."将ip地址分为了4部分,各部分对应的权值为256^3, 256^2, 256, 1,相成即可 int类型转ip地址,思路类似,除以权值即可,但是有部分字符串的操作 实现代码 #include &l

  • C++中Operator类型强制转换成员函数解析

    类型转换操作符(type conversion operator)是一种特殊的类成员函数,它定义将类类型值转变为其他类型值的转换.转换操作符在类定义体内声明,在保留字 operator 之后跟着转换的目标类型.转换函数又称类型强制转换成员函数,它是类中的一个非静态成员函数.它的定义格式如下: 复制代码 代码如下: class <类型说明符1> { public: operator <类型说明符2>(); - } 这个转换函数定义了由<类型说明符1>到<类型说明符2

  • 讲解C++中的枚举类型以及声明新类型的方法

    C++枚举类型 如果一个变量只有几种可能的值,可以定义为枚举(enumeration)类型.所谓"枚举"是指将变量的值一一列举出来,变量的值只能在列举出来的值的范围内.声明枚举类型用enum开头.例如: enum weekday{sun, mon, tue, wed, thu, fri, sat}; 上面声明了一个枚举类型weekday,花括号中sun, mon, -, sat等称为枚举元素或枚举常量.表示这个类型的变量的值只能是以上7个值之一.它们是用户自己定义的标识符. 声明枚举类

  • 解析C++中四种强制类型转换的区别详解

    C++的四种强制类型转换,所以C++不是类型安全的.分别为:static_cast , dynamic_cast , const_cast , reinterpret_cast为什么使用C风格的强制转换可以把想要的任何东西转换成合乎心意的类型.那为什么还需要一个新的C++类型的强制转换呢?新类型的强制转换可以提供更好的控制强制转换过程,允许控制各种不同种类的强制转换.C++中风格是static_cast<type>(content).C++风格的强制转换其他的好处是,它们能更清晰的表明它们要干

  • 浅谈C++中的string 类型占几个字节

    在C语言中我们操作字符串肯定用到的是指针或者数组,这样相对来说对字符串的处理还是比较麻烦的,好在C++中提供了 string 类型的支持,让我们在处理字符串时方便了许多. 首先,我写了一段测试代码,如下所示: 复制代码 代码如下: #include <iostream>using namespace std; int main(void){ string str_test1; string str_test2 = "Hello World"; int value1, val

  • 深入理解:Java是类型安全的语言,而C++是非类型安全的语言

    有过C++开发经验的人会发现,我们可以将0作为false,非零作为true.一个函数即使是bool类型的,但是我们还是可以返回int类型的,并且自动将0转换成false,非零转换成true.代码实例如下: 复制代码 代码如下: #include<iostream> #include<stdlib.h> using namespace std; bool fun()//函数返回类型是bool,但是我们在函数中可以返回int类型. {     return 1; } void main

随机推荐