派生类对象也是基类对象。这意味着在使用基类的地方可以用派生类来替换。
- 当派生类以public方式继承基类时,编译器可自动执行的转换(向上转型 upcasting 安全转换)
①、派生类对象指针自动转化为基类对象指针。
下面用代码进行说明:
#include <iostream> #include <string> using namespace std; class Employee {//员工类 public: Employee(const string& name, const int age, const int deptno) : name_(name), age_(age), deptno_(deptno) { } private: string name_; int age_; int deptno_; }; class Manager : public Employee {//经理类 public: Manager(const string& name, const int age, const int deptno, const int level) : Employee(name, age, deptno), level_(level) { } private: int level_; }; int main(void) { Employee e1("zhangsan", 25, 20); Manager m1("lisi", 35, 20, 10); Employee* pe; Manager* pm; pe = &e1;//基类指针指向派生类对象 pm = &m1;//派生类指针指向派生类对象 return 0; }
以上代码都可正常编译:
编译是可正常通过的,那如果反过来,将派生类指针指向基类对象?
②、派生类对象引用自动转化为基类对象引用。
③、派生类对象自动转换为基类对象(特有的成员消失)。 - 当派生类以private/protected方式继承基类时
④、派生类对象指针(引用)转化为基类对象指针(引用)需用强制类型转化。但不能用static_cast,要用reinterpret_cast。
#include <iostream> #include <string> using namespace std; class Employee {//员工类 public: Employee(const string& name, const int age, const int deptno) : name_(name), age_(age), deptno_(deptno) { } private: string name_; int age_; int deptno_; }; class Manager : public Employee {//经理类 public: Manager(const string& name, const int age, const int deptno, const int level) : Employee(name, age, deptno), level_(level) { } private: int level_; }; class Manager2 : private Employee {//经理类 public: Manager2(const string& name, const int age, const int deptno, const int level) : Employee(name, age, deptno), level_(level) { } private: int level_; }; int main(void) { Employee e1("zhangsan", 25, 20); Manager m1("lisi", 35, 20, 10); Manager2 m2("wangwu", 40, 15, 8); Employee* pe; Manager* pm; Manager2* pm2; pe = &e1;//基类指针指向派生类对象 pm = &m1;//派生类指针指向派生类对象 pm2 = &m2; pe = &m1;//派生类指针可以转化为基类指针 //pm = &e1;//基类指针无法转化为派生类指针,无法将基类对象看成是派 e1 = m1;//派生类对象自动转换为基类对象,将派生类对象看成基类对象 //会产生对象切割(派生类特有成员消失)。其对象切割英文为:object slicing pe = &m2; return 0; }
编译:
⑤、不能把派生类对象强制转换为基类对象。
编译:
另外还可以用C的方式强制进行转换:
说到这里,来回顾一下static_cast、reinterpret_cast、cost_cast、dynamic_cast的使用场景:
static_cast:用于编译器认可的静态转换,比如从char到int、从double到int,或者具有转换构造函数,或者重载的类型转换运算符。
reinterpret_cast:用于编译器不认可的静态转换,比如int*转为int,在转型的过程中,不做任何对齐。
const_cast去除常量性。
以上都是静态转换,不需要运行时的支持,下面的这个是动态转换,需要运行时的支持:
dynamic_cast:用于动态转换,安全的向下转型。这个在之后的多态会学习~
那如果强制转换行不行呢?
- 基类对象指针(引用)可用强制类型转换为派生类对象指针(引用), 而基类对象无法执行这类转换。
但是可以通过强制转换:
那如果是对象,而不是指针呢?
这样不行,那可以用强制转换么? - 向下转型不安全,没有自动转换的机制。
为什么不安全呢?因为指针是指向基类的,但是强制转换成了派生类指针,则就有可能去访问派生类的特定成员,如level_,但是它不属于基类的,所以就不安全了。
从语法上来说,有没有办法能实现基类对象转换成派生类对象呢?答案是肯定的,下面来实现一下,仅仅是说明一下语法,没有任何实际意义:
方法一:利用转换构造函数来实现:
#include <iostream> #include <string> using namespace std; class Employee {//员工类 public: Employee(const string& name, const int age, const int deptno) : name_(name), age_(age), deptno_(deptno) { } private: string name_; int age_; int deptno_; }; class Manager : public Employee {//经理类 public: Manager(const string& name, const int age, const int deptno, const int level) : Employee(name, age, deptno), level_(level) { } Manager(const Employee& other) : Employee(other), level_(-1) {//转换构造函数,没有意义,仅说明语法 } private: int level_; }; int main(void) { Employee e1("zhangsan", 25, 20); Manager m1("lisi", 35, 20, 10); m1 = e1; return 0; }
编译:
方法二:类型转换运算符重载:#include <iostream> #include <string> using namespace std; class Manager;//由于Employee用到了Manager,而它在后面声明的,所以需要做个前向声明 class Employee {//员工类 public: Employee(const string& name, const int age, const int deptno) : name_(name), age_(age), deptno_(deptno) { } operator Manager(); private: string name_; int age_; int deptno_; }; Employee::operator Manager() { return Manager(name_, age_, deptno_, -1); } class Manager : public Employee {//经理类 public: Manager(const string& name, const int age, const int deptno, const int level) : Employee(name, age, deptno), level_(level) { } private: int level_; }; int main(void) { Employee e1("zhangsan", 25, 20); Manager m1("lisi", 35, 20, 10); m1 = e1; return 0; }
编译:
由于Manager是在运算符重载方法之后申明的,所以编译通不过,这时可以将它放到Manager声明之后:
这样就可以编译通过了,由于在代码是写在同一个类中,所以就存在先后顺序的问题,如果类是分开在不同的cpp中定义的,就不会存在这个问题了。