(P31)继承:转换与继承 ,派生类到基类的转换 ,基类到派生类的转换

1.转换与继承

  • 派生类对象也是基类对象。这意味着,在使用基类的地方可以用派生类来代替。
    eg:教师类继承至人类,那么教师也是人

2.派生类到基类的转换

  • 派生类和基类谁大?基类比派生类的适用集合更大,派生类是基类的具体化
  • 当派生类以public方式继承基类时,编译器可自动执行的转换(向上转型upcasting安全转换)
    (1)派生类对象指针自动转化为基类对象指针
    基类指针指向某某,这样去理解
    (2)派生类对象引用自动转化为基类对象引用
    (3)派生类对象自动转换为基类对象(派生类特有的成员消失)
  • 当派生类以private/protected方式继承基类时
    (1)派生类对象指针(引用)转化为基类对象指针(引用)需用强制类型转化。但是不能用static_cast,要用reinterpret_cast
    (2)不能把派生类对象强制转换为基类对象
  • eg:P31\01.cpp
#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, int level)
        : Employee(name, age, deptno), level_(level)
private:
    int level_;
};

class Manager2 : private Employee
{
public:
    Manager(const string& name, const int age, const int deptno, int level)
        : Employee(name, age, deptno), level_(level)
private:
    int level_;
};


int main(void)
{
    Employee el("zhangsan", 25, 20);
    Manager ml("lisi", 38, 20, 10);
    Manager2 m2("wangwu", 40, 15, 8);
    Employee* pe;
    Manager* pm;
    Manager2* pm2;

    pe = &el;//基类指针指向基类对象
    pm = &ml;//派生类指针指向派生类对象
    pm2 = &m2;


    //基类指针指向派生类对象,换句话就是:派生类对象指针可以转化为基类指针。将派生类对象看成基类对象
    pe = &ml;
    pm = &el;//error,基类指针无法转化为派生类指针,无法将基类对象看成是派生类对象。
            //基类比派生类的适用集合更大,派生类是基类的具体化

    el = ml;//派生类对象可以转化为基类对象。因为可以将派生类对象看成是基类对象
            //对象转化,会产生对象切割object slicing
            //调用赋值运算符完成的

    //派生类指针转换为基类指针
    //私有或保护继承时,生类对象指针不可以转化为基类指针
    pe = pm2;//等价于pe = &pm2;因为&pm2的类型也是Manager*,都是error
    pe = static_cast<Employee*>(pm2);//error,这里的转化编译器不认识这
    pe = (Employee*)(pm2);//C风格的强制转化是可以的
    pe = reinterpret_cast<Employee*>(pm2);//是可以的

    // e1 = m2;//私有或保护继承时,派生类对象不可以转化为基类对象,error,自动或强制转化也不行,如下:
    // e1 = reinterpret_cast<Manager2>(pm2);//error
    // e1 = (Manager2)pm2;//error


    //基类指针转化为派生类指针
    // pm = pe;//error
    pm = static_cast<Manager*>(pe);//是可以的,但是不安全,pe指向的是基类对象,但是pm可以访问level_,该成员基类是没有的
    pm2 = static_cast<Manager2*>(pe);//是可以的

    //基类对象无法强制转化为派生类对象
    ml = el;//error
    ml = static_cast<Manager>(el);//error
    ml = reinterpret_cast<Manager>(el);//error

    return 0;
}

/*
回顾各种转化
静态转化:3种:
static_cast 用于编译器认可的静态转化,eg:从char到int,从double到int,或者具有转换构造函数,或者
重载了转换类型运算符
reinterpret_cast 重新解释,用于编译器不认可的静态转换。eg:从int *转为int,在转型的过程中,不做任何对齐操作
const_cast 去除常量性

动态转化:1钟:
dynamic_cast 用于动态转换,安全的向下转型。用于多态
*/

小结

  • 对于public或者private继承的向上或者向下的指针强转而言,都是可以成功的,但是向下强转可能不安全
  • 对于public或者private继承的向上或者向下的类对象转换而言,向下转型都不可以(除非使用转换构造或者重载类型转换运算符),向上转型只有public可以,但是可能不安全

3.基类到派生类的转换

  • 基类对象指针(引用)可用强制类型转换为派生类对象指针(引用),而基类对象无法执行这类转换

  • 向下转型不安全,且没有自动转换的机制

  • 其他说明:
    基类对象转换为派生类对象是可以转换的,但是往往不去这样实现
    这样实现,会带来很多的语义错误
    语法上是可以转换的
    方法有2个:
    (1)转换构造函数:将其他类型转换为类类型,P31\02.cpp
    (2)类型转换运算符重载:将类类型转换为其他类型,P31\03.cpp

  • eg:P31\02.cpp

#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, int level)
        : Employee(name, age, deptno, level), level_(level)

    //从语法上来演示基类对象可以转化为派生类对象,但是没有意义
    //带一个参数的构造函数,称之为转换构造函数
    Manager(const Employee& other) : Employee(other), level_(-1) 
    {

    }
private:
    int level_;
};

//基类对象转换为派生类对象是可以转换的,但是往往不去这样实现
//这样实现,会带来很多的语义错误
//语法上是可以转换的

int main(void)
{
    Employee el("zhangsan", 25, 20);
    Manager ml("lisi", 38, 20, 10);

    //方法(1):转换构造函数:将其他类型转换为类类型
    m1 = e1;
    return 0;
}
  • eg:P31\03.cpp
#include <iostream>
#include <string>
using namespace std;

class Manager;//因为operator Manager() ,所以需要前向声明
class Employee
{
public:
    Employee(const string& name, const int age, const int deptno) : name_(name),
        age_(age), deptno_(deptno)
    {

    }
    //将Employee转换为Manager类型
    operator Manager();

    //下面这样写是错的,因为在这俩类在同一个文件中,只有Manager的前向声明,看不到Manager的定义,也就看不到Manager类的构造函数,error
    // operator Manager()
    // {
    //     return Manager(name_, age_, deptno_, -1);
    // }


private:
    string name_;
    int age_;
    int deptno_;//部门号
};

//经理类继承至员工类
class Manager : public Employee
{
public:
    Manager(const string& name, const int age, const int deptno, int level)
        : Employee(name, age, deptno, level), level_(level)

    Manager(const Employee& other) : Employee(other), level_(-1) 
    {

    }
private:
    int level_;
};

//仅仅是从语法上演示基类对象可以转化为派生类对象,但是没有意义
//目的是要理解转换构造函数与类型运算符重载的区别
//把它放在Manager类的定义之后,这样才能看到Manager的构造函数
Employee::operator Manager()
{
    return Manager(name_, age_, deptno_, -1);//构造Manager对象
}


int main(void)
{
    Employee el("zhangsan", 25, 20);
    Manager ml("lisi", 38, 20, 10);

    //方法(2):类型转换运算符重载:将类类型转换为其他类型
    //将基类对象转换为派生类对象
    m1 = e1;
    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

喜欢打篮球的普通人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值