C++子类 父类的相互转换 和 虚函数

来源:互联网 发布:指南针软件卸载 编辑:程序博客网 时间:2024/06/03 22:59

今天在程序中遇到一个问题,关于子类 父类的强制转换的。查了下网络,大概弄懂了些,记录下来作为笔记。


       先看一个例子

引自雁南飞的博客】在C++的世界中有这样两个概念,向上类型转换,向下类型转换,分别描述的是子类向基类和基类向子类的强制类型转换。

向上强制类型转换

切割:覆盖方法和子类数据丢失的现象生成切割(slice)。

#include "stdafx.h"#include <iostream>using namespace std;class Base{public:int b;virtual void Test(){cout << "base" <<endl;}};class Derived:public Base{public:int tt;int d;virtual void Test(){cout << "derived" <<endl;}virtual void check(){cout << "derived check" <<endl;}};int main(){Derived d;d.d = 1;d.tt = 2;Base b = d;//直接赋值(产生切割)b.Test();Base& b2 = d;//使用引用赋值(不产生切割)b2.Test();Base* b3 = &d;//使用指针赋值(不产生切割)b3->Test();//b3->check();system("PAUSE");return 1;}

然后我们看监视的结果:

(1)对象层面:


在对象上,我们可以看到所有的转换都出现了阉割(毋庸置疑的,由于子类的内存比父类大,转换为父类肯定会“丢失”信息,只会保留相同的东西)。另外由于虚函数的原因,引用和指针都指向子类,而直接对象的转换则直接指向了父类。

(2)地址层面


从地址上看,采用引用和指针的转换地址和原始子类一致,直接对象的转换则地址 被改变减少了16个字节(为什么? 没搞为什么事减? 为什么是16? 希望大神能解答).

因此,我们得出结论,在向上强制转换过程中,使用指针和引用不会造成切割,而使用直接赋值会造成切割。

向下强制类型转换

使用dynamic_cast进行向下强制类型转换。使用此关键字有一下几个条件:

1、源类型必须有虚函数
2、必须打开编译器的RTTI开关(vc6: progect-> settings -> c/c++ tab ->category[c++ language]-> Enable RTTI);
3.必须有继承关系。

#include <iostream>
using namespace std;

class CPoint
{
public:
virtual void f(){cout << "CPoint::f" <<endl;}
};

class CPoint3D : public CPoint
{
public:
virtual void f(){cout << "CPoint3D::f" <<endl;}
};

int main() 
{
CPoint point,*pPoint; 
CPoint3D point3D,*pPoint3D;


//子类向父类类型转换,直接强制转换就可以了,但实际仍然指向子类对象
pPoint = (CPoint*)&point3D;
pPoint->f(); //调用的仍然是子类的函数,这便是虚函数的魅力所在

//父类向子类类型转换,要用dynamic_cast
pPoint3D = dynamic_cast(pPoint);
pPoint->f(); //pPoint实际指向一个CPoint3D对象,故可以正确转换

pPoint3D = dynamic_cast(&point);
cout << (int*)pPoint3D << endl; //&point指向的不是一个CPoint3D对象,返回值为NULL
return 0;
}
运行结果为:
CPoint3D::f
CPoint3D::f
00000000

这个问题牵扯到c++的对象模型。一般认为子类对象大小>=父类对象大小。为什么?因为子类可以扩展父类,可以增加成员变量。如果一个子类增加了成员变量,那么它的对象的内存空间会大于父类对象。这时一个实际指向父类的指针,如果被强制转化为子类对象指针,当使用这个指针时可能会导致越界访问非法内存。相反,为何子类指针可以转换为父类指针?因为父类指针需要的,子类对象都有,不会出现非法内存访问。
这就是dynamic_cast不一定成功的原因。如果一个实际指向子类对象的指针被转换成了父类指针,然后再用dynamic_cast转换回来,一定能成功,而一个实际指向父类对象的指针,被dynamic_cast转换为子类指针,一定会失败。

因此,我们可以使用dynamic_cast来判断两个类是否存在继承关系。


0 0
原创粉丝点击