类其实也是一种数据类型,也可以发生数据类型转换,不过这种转换只有在基类和派生类之间才有意义,并且只能将派生类赋值给基类,包括将派生类对象赋值给基类对象、将派生类指针赋值给基类指针、将派生类引用赋值给基类引用,这在 C++ 中称为向上转型(Upcasting)。相应地,将基类赋值给派生类称为向下转型(Downcasting)。
向上转型非常安全,可以由编译器自动完成;
向下转型有风险,需要程序员手动干预(有时编译器认为只是错误)
赋值的本质是将现有的数据写入已分配好的内存中,对象的内存只包含了成员变量,所以对象之间的赋值是成员变量的赋值,成员函数不存在赋值问题。
将派生类对象赋值给基类对象时,会舍弃派生类新增的成员,所以不存在安全问题。
即使将派生类对象赋值给基类对象,基类对象也不会包含派生类的成员,所以依然不能通过基类对象来访问派生类的成员。
这种转换关系是不可逆的,只能用派生类对象给基类对象赋值,而不能用基类对象给派生类对象赋值。理由很简单,基类不包含派生类的成员变量,无法对派生类的成员变量赋值。同理,同一基类的不同派生类对象之间也不能赋值。
用派生类对基类的引用或者指针赋值决计不会发生截至的现象
#include <iostream> #include <string> class Super { public: Super(); Super(std::string &initialString); virtual ~Super(); public: virtual void getString() const; private: std::string SuperString; }; class Sub : public Super { public: Sub(); Sub(std::string &initialString); virtual ~Sub(); public: virtual void getString() const override ; private: std::string SubString; }; Super::Super() :SuperString("") { } Super::Super(std::string &initialString) :SuperString(initialString) { } Super::~Super() { } void Super::getString() const { std::cout << SuperString << std::endl; } Sub::Sub() :SubString("") { } Sub::Sub(std::string &initialString) :SubString(initialString) { } Sub::~Sub() {} void Sub::getString() const { std::cout << SubString << std::endl; } int main() { std::string str1 = "Sub",str2 = "Super"; std::string str3 = "Sub2",str4 = "Super2"; Sub sub(str1); Super & super = sub; //向上转型,派生类赋值给父类的引用或者父类的指针都是安全的 super.getString(); Super super1(str4); Sub *sub1 = static_cast<Sub*>(&super1); sub1->getString(); return 0; }
Sub
S
Super2
然而就算是有时候用向下转换,也得用dynamic_cast<>的类型转换。这样做的好处就是:当指针dynamic_cast<>失败的话,将会指向nullptr,而不是其他的无意义的数据;当针对对象引用失败的时候就会抛出std::bad_cast异常
Super *inSuper; Sub *sub1 = dynamic_cast<Sub*>(inSuper); if(sub1 != nullptr) { std::cout << "sub1 is not nullptr" << std::endl; } else { std::__throw_bad_cast(); }
程序会报错!!!