原文:https://blog.csdn.net/baidu_35679960/article/details/80821222
1、隐式转型(向上转型,即将派生类对象赋值给基类)
C++允许向上转型,即将派生类的对象赋值给基类的对象是可以的,其只不过是将派生类中基类的部分直接赋给基类的对象,这称为向上转型(此处的“上”指的是基类),例如:
class Base{ };
class Derived : public base{ };
Base* Bptr;
Derived* Dptr;
Bptr = Dptr; //编译正确,允许隐式向上类型转换
Dptr = Bptr;//编译错误,C++不允许隐式的向下转型;
但是编译正确不代表能够使得程序安全运行。这很好理解,如果你把一个指向Base类对象的指针 赋值 给一个Drived类类型的指针,如果这个指针去访问Drived类中存在而Base类中不存在成员,很明显就不安全了。
所以一个更好的办法是使用dynamic_cast;
2、向下转型
正如上面所述,类层次间的向下转型是不能通过隐式转换完成的。此时要向达到这种转换,可以借助static_cast 或者dynamic_cast。
2.1 通过static_cast完成向下转型
例如:
class Base{ };
class Derived : public base{ };
Base* B;
Derived* D;
D = static_cast<Drived*>(B); //正确,通过使用static_cast向下转型
需要注意的是:static_cast的使用,当且仅当类型之间可隐式转化时,static_cast的转化才是合法的。有一个例外,那就是类层次间的向下转型,static_cast可以完成类层次间的向下转型,当时向下转型无法通过隐式转换完成!
2.2 通过dynamic_cast完成向下转型
和static_cast不同,dynamic_cast涉及运行时的类型检查。如果向下转型是安全的(也就是说,如果基类指针或者引用确实指向一个派生类的对象),这个运算符会传回转型过的指针。如果downcast不安全(即基类指针或者引用没有指向一个派生类的对象),这个运算符会传回空指针。
ps:要使用dynamic_cast类中必须定义虚函数
class Base{
public:
virtual void fun(){}
};
class Drived : public base{
public:
int i;
};
Base *Bptr = new D;//语句0
Derived *Dptr1 = static_cast<Derived *>(Bptr); //语句1;
Derived *Dptr2 = dynamic_cast<Derived *>(Bptr); //语句2;
此时语句1和语句2都是安全的,因为此时Bptr确实是指向的派生类,虽然其类型被声明为Base*,但是其实际指向的内容确确实实是Drived对象,所以语句1和2都是安全的,Dptr1和Dptr2可以尽情访问Drived类中的成员,绝对不会出问题。
但是此时如果将语句0改为这样:
Base *Bptr = new B;
那语句1就不安全了,例如访问Drived类的成员变量i的值时,将得到一个垃圾值。(延后了错误的发现)
语句2使得Dptr2得到的是一个空指针,对空指针进行操作,将发生异常,从而能够尽早的发现错误,这也是为什么说dynamic_cast更安全的原因。
2.3多继承时的向下转型
class Base1{
virtual void f1(){}
};
class Base2{
virtual void f2(){}
};
class Derived: public Base1, public Base2{
void f1(){}
void f2(){}
};
Base1 *pD = new Derived;
Derived *pD1 = dynamic_cast<Derived*>(pD); //正确,原因和前面类似
Derived *pD2 = static_cast<Derived*>(pD); //正确,原因和前面类似
Base2 *pB1 = dynamic_cast<Base2*>(pD); //语句1
Base2 *pB2 = static_cast<Base2*>(pD); //语句2
此时的语句1,将pD的类型转化为Base2*,即:使得pB1指向Drived对象的Base2子对象,为什么能达到这种转化?因为dynamic_cast是运行时才决定真正的类型,在运行时发现虽然此时pD的类型是Base1*,但是实际指向的是Derived类型的对象,那么就可以通过调整指针,来达到pB1指向Derived 对象的Base2子对象的目的;
但是语句2就不行了,其使用的是static_cast,它不涉及运行时的类型检查,对于它来讲,pD的类型是Base1*,Base1和Base2没有任何关系,那就会出现编译错误了。error: invalid static_cast from type ‘Base1*’ to type ‘Base2*’
总结:对于多种继承,如果pD真的是指向Derived,使用static_cast和dynamic_cast都可以转化为Derived,但是如果要转化为Base1的兄弟类Base2,必须使用dynamic_cast,使用static_cast不能通过编译。
ps:因为Derived*和Base1*和Base2*之间存在隐式转化,可以将语句2修改为:
Base2 *pB2 = static_cast<Base2*>(static_cast<Derived*>(pD));
这样就可以完成转换。