继续(三)

 派生类对象也是基类对象。这意味着在使用基类的地方可以用派生类来替换。

  • 当派生类以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中定义的,就不会存在这个问题了。

原文地址:https://www.cnblogs.com/webor2006/p/5559030.html