1.父类指针无法直接调用子类的新函数,需要转换为子类指针后才能调用。
c++编译器在编译的时候是做静态类型分析,父类指针是否真的指向一个子类类型,编译器并不会做这个假设。因此用父类的指针去调用子类的函数无非被识别,这里有一种安全和不安全的方式实现这种转化。
case1:不安全的方式
class Base
{
public:
void vritual Func()
{
cout<<"Base Func\n";
}
}
class Child :public Base
{
public:
void Func()
{
cout<<"Child Func\n";
}
void NewFunc()
{
cout<<"Child Newfunc\n";
}
}
//测试用例
int main()
{
Base *b=new Child;
b->Func();//输出Child Func
//b->NewFunc();//错误 这个函数不是虚函数,不能多态调用
Child *child=(Child *)b;//方式一不安全
child->NewFunc();//Child NewFunc
Child *child=dynamic_cast<Child *>(b);//方式二安全方式
if (child!=NULL)
{
child->NewFunc();//Child NewFunc
}
return 0;
}
备注:
1.方式一强制转换方式不安全
不安全是因为转换的时候无法得知是否转换成功。编译器强制把b当成Child类型去使用。比如说b本来是真的指向Base而不是Child类型那么强制转换后调用Child的NewFunc可能会导致程序崩溃。
2.方式二:动态转换方式
dynamic_cast是在运行时去做转换而非编译时,所以它可以给出是否转换成功的信息。如果转换不成功则返回NULL。所以可以判断转换结果是否为NULL来决定是否能使用该指针不会导致程序崩溃
转换情况二:
父类不存在虚函数:
#include <iostream>
using namespace std;
typedef void(*Fun)(void);
class Base
{
public:
Base(int i):a(i){b = 3;}
void virtual fun() {cout << "Base's funciton" <<endl;}
void this_fun(Base *b)
{
if (b == this)
cout << "it is Base's this" <<endl;
}
private:
int b;
int a;
};
class Derived: public Base
{
public:
Derived():Base(2),d(0){}
void fun() { cout << "Derived's function" <<endl;}
void this_fun(Derived *d)
{
if (d == this)
cout << "it is Derived's this" <<endl;
}
private:
int d;
};
int main()
{
Base b(1);
Derived *d=(Derived*) &b;
(*d).this_fun(d);//输出: it is Derived‘s 证明:d指针仍然为指向子类
cout<<*((int *)d)<<endl;//输出:Base 的成员b的值 3 证明:d指向的仍然为父类对象
cout<<*((int *)d+1)<<endl;// 输出:Base 的成员a 的值 1 证明:d指向的仍然为父类对象
d->fun();//d 为指向子类的指针
system("pause");
return 0;
}
注意:1. *((int *)d) 是将对象指针强转,访问第一个成员 ((int *)b+1)访问对象的第二个成员变量
int main()
{
Base b(1);
Derived *d=(Derived*) &b;
(*d).this_fun(d);//输出的:it is Derived's this 证明:this指针仍然为指向子类
Fun f1=(Fun)*(int*)*(int *)d; //定义一个函数指针 值为d的虚函数
f1();//输出:Base’s function 证明:f1指向父类虚函数表的第一个函数
cout<<*((int *)d)<<endl;//输出:一个一个地址值 17384060
cout<<*((int *)d+1)<<endl;// 输出:Base 的成员b 的值 3 证明:d所指向的对象仍然为父类对象
cout<<*((int *)d+2) <<endl; //输出:Base的成员a 的值 1 证明:d所指向的对象仍然为父类对象
d->fun();//fun是虚函数,因此寻找虚函数表的第一个
system("pause");
return 0;
}
分析:
1.Fun f1=(Fun)*(int*)*(int *)d;
从右往左分析 (int *)d 转换为一个int 类型的指针,*(int *)d,获取指针所指的内容,其实存放的是地址, 即虚函数表地址(表第一项地址)
(int *)*(int *)d 地址强转一下,*(int *)*(int *)d,访问虚函数表的第一项,其实存放的也是指针,(Fun)强转为函数指针 ;
2.存在虚函数,此时对象布局发生改变