c++学习笔记(19.动态类型识别)(一)

2014-11-24 09:43:12 · 作者: · 浏览: 3

本节知识点:

1.动态类型识别的前因:

a.首先我们先说类之间的几种强制类型转换: 第一、具有继承关系的类对象之间的转换(即子类对象与父类对象之间,或叫对象之间),由于赋值兼容性原则,child c1; parent p1 = c1; 子类是特殊的父类,因为子类是大于等于父类的,所以将子类对象赋值给父类对象,是安全的( 将大的赋值给小的是安全的,这样由于默认强制类型转换,不会造成访问到未知数据,造成未知错误 )。编译器会默认将子类中父类没有成员砍掉,这个过程就是强制类型转换的过程,其实是默认强制类型转换,所以可以省略parent p1 = (parent) c1; 或者 parent p1 = static_cast (c1); 的过程。上面一种是c方式的强制类型转换,另一种是c++方式的强制类型转换,其实实质是一样的,都是砍掉多的成员!!!当然也会出现相反的情况,即将父类对象赋值给子类对象,这个过程就需要强制类型转换了,否则会编译出错,因为这个过程不是编译器默认的! child c2; parent p1 = c2; child c1 = (child&)p1;或 child c1 = static_cast (p1);这个过程c2对象是不会赋值给c1的,因为当c2赋值给p1的时候,p1会丢失一部分成员,丢失的那部分是传递不到c1中的,所以c1中就会出现未知数据,所以这样的语句是错误的,也是没有意义的,容易造成未知错误,应该避免!!!
第二、 具有继承关系的类对象指针之间的转换(即子类对象指针与父类对象指针之间,或叫指针引用之间,因为引用实质上是一种指针),由于赋值兼容性原则,child* c1; parent* p1= c1;这个过程中隐藏了一个强制类型转换的过程,即parent* p1 = (parent*) c1; 或 parent* p1 = static_cast (c1); 或 parent* p1 = static_cast (c1);(注:两者区别后面再详细说)。同理子类指针赋值给父类指针也是安全的,因为当通过父类指针去寻找成员的时候,只能找到父类中有的成员,其实也就等同了砍掉了子类中父类没有的成员了,因为多的这些子类特有成员,通过父类p1访问不到!!!但无论怎么说指针之间的强制类型转换后,仅仅了改变了指针的行为(因为指针类型变了,类型决定行为),但是p1和c1中存放的数值是相同的,即地址是相同的!但是对象之间的强制类型转换后,可是实实在在的把多余的部分砍掉了!!!这是两者最大的区别,也就是因为这个区别,才造成了需要动态类型识别!!!这样当把父类对象指针赋值给子类对象指针的时候,即parent* p1; child* c1 = (child*)p1;,就不能像对象之间一样,判断这个语句是绝对错误的!!!为什么?因为你不能确定p1的内容是什么,当parent* p1 = &c2; p1指向一个子类对象的时候,这几条语句就是对的!但当parent* p1 = &p2; p1指向一个父类对象的时候,这几条语句就是错的!所以说需要动态类型检查!!!这里再说说把父类对象指针强制类型转换成子类对象指针的语句,child* c1 = (child*)p1; 或者 child* c1 = static_cast (p1); 或者 child* c1= dynamic_cast (p1);(区别后面说)
第三、没有继承关系的类对象之间的转换,是一点意义都没有的,只能使用c方式的野蛮转换,test t2; child c2 = (child&)t1;
第四、没有继承关系的类对象指针之间的转换,同样也是一点意义都没有的,也只能使用c方式的野蛮转换,test* t1 = NULL; child* c2 = (child*)t1; 示例代码:
#include 
  
   
#include 
   
     using namespace std; class parent { public: int a; }; class child : public parent { public: int b; }; class test { }; int main() { child c1; c1.a = 10000; c1.b = 20000; child* cp1 = &c1; parent* pp1 = cp1; //由于赋值兼容性原则 所以默认强制类型转换 parent* pp2 = (parent*)cp1; //此语句与上面语句一样 parent* pp3 = static_cast
    
     (cp1); //此语句与上面语句一样 child* cp2 = (child*)pp1; cout << "cp2 " << cp2->a << " " << cp2->b << endl; child* cp3 = static_cast
     
      (pp1); cout << "cp3 " << cp3->a << " " << cp3->b << endl; /*没有继承关系的类之间的强制类型转换,只能用
      c语言的方式,static_cast报错*/ /*因为没有继承关系的类之间的强制类型转换,无论是指针之间的,还是变量之间的,都没有意义*/ test* t1 = NULL; child* c2 = (child*)t1; // child* cc2 = static_cast
      
       (t1); test t2; child c3 = (child&)t2; // child c4 = static_cast
       
        (t2); return 0; } 
       
      
     
    
   
  
注意:通过上面分析,出现了一个问题,就是在对象之间进行强制类型转换的时候,parent p1 = (parent) c1; 把子类对象赋值给父类对象的时候,把c1强制类型转换成为parent类型就可以了,但是当反过来,child c1 = (child&) p1; child c1 = static_cast (p1); 把父类对象赋值给子类对象的时候,就必须要把p1强制类型转换成为child的引用类型!!!为什么一定要是引用类型?不明白!!!不是引用就报错!!!

2.动态类型:

a.由于基类指针可以直接指向派生类对象,因此可能存在指针所指类型与具体指向的对象类型不同的情况,如parent* p1 = &c1; 或者 parent* p1 = new child; 动态类型指的是基类指针所指向的对象的实际类型!即p1指向的到底是父类对象还是子类对象! 所以:动态类型导致,child *c2 = (child*) p1; 这样的语句不知道是否正确,当p1的动态类型为child时,(child* )p1强制类型转换成功,否则,可能出现无法预知的错误!即基类指针是否可以强制类型转换为子类指针取决于动态类型!

3.动态类型的识别(多态的方式):

a.利用多态进行动态类型识别:c++中的多态根据实际的对象类型调用对应的虚函数,可以在基类中定义虚函数返回具体的类型信息,所有的派生类都必须实现类型相关的虚函数,每个类中的类型虚函数都需要不同的实现。 示例代码:
#include 
  
   

using namespace std;

class parent
{
public:
	/*如果想在类中定义一个常量并且完成对常量的赋值
	  第一种,static const int ID = 0;使用静态常量
	  第二种,const int ID; 然后通过构造函数的初始化列表赋值  使用const常量
	  第三种,enum{ ID = 0 }; 使用枚举常量 
	  注意:静态变量其实是种特殊的全局变量  静态常量其实就是特殊的全局常量
	  静态变量要在类外面进行初始化,静态常量由于是常量要在定义处初始化,所以不在外面初始化 
	*/
	enum{ ID = 0 };
	virtual int type()
	{
		return ID;
	}
};

class child : public parent
{
public:
	en