C++ 关于派生类的使用(课程笔记5.12)

Part7.Derived Classes

数据抽象:现实生物中的共同属性抽取出来,来建立模型。

继承性和封装性是面向对象的真谛,所以在这之前要重点掌握!!!

CLASS relationships:

IS A       --  By Inheritance    e.g:”a manager is an employee

 HAS A   --  By Composition e.g:   ”a person has a name

派生类:由已有的基类基础上派生出来的。

例子:A program dealing with people employed by a company

  问题在于一个公司会有中层经理,但也会有更高层经理,而list中数据类型是不能变化的。显然这两类是没法区分的。

所以我们要告诉编译器,经理既是员工同时也是经理,这样才能放入group中。

 struct Manager : public Employee
 {
     list<Employee*> group;
     short level;
   …
 };

        

Base class – Employee

Derived class – Manager

A derived object can be used wherever a base class is acceptable.

 接下来我们就可以用一个derived的指针去指向一个base的指针,但是不能反向指向,

问题很明显:派生类的成员,基类并不是都有的,就会出现灾难性的访问error!

当进行地址赋值的时候,要注意强制类型转换static_cast。

 void g(Manager mm, Employee ee)
{ 
    Employee* pe{&mm};    //coercion
    Manager* pm{&ee};     //error
    pm->level=2;                //disaster
    pm=static_cast<Manager*>(pe);
    pm->level=2;
}
A class must be defined before it is used as a base class.
Example:
  class Employee;//this is a declaration

  class Manager:public Employee{

          …    // error, Employee must be

                // defined before it is used.

 };

A member function of a derived class can use public and protected members of its base class as if they were declared in the derived class itself.
A member function of the derived class cannot use private members of the base class.

基类的public属性在被继承的派生类中就会被转化为protected

protected是不能外界访问的。

基类的同名函数是不会被派生类的函数重载的,只会被覆盖,想要调用则需要加上解析运算符。

void Manager::print() const

{

   Employee::print();   // print Employee infor mation

   cout << level << … ;

}

构造函数怎么写?

还要注意如果基类具有构造函数,则必须调用构造函数。 基类构造函数的参数在派生类构造函数的定义中指定为初始值设定项。 构造函数永远不会被继承。

•If a class has several members of other classes and has a base class, its constructor
is written like:

classname::classname(parameterlist)

    : baseclassname(argumentlist),

      member1(argumentlist ),   

      member2(argumentlist)…

   {

     …

    }

实现方法:

class Employee

{     …

     Employee(const string& n,Date dd,int d);

      …

};

class Manager : public Employee

{    …

     Manager(const string& n,Date dd,int d,int lvl);

     …

};

Employee::Employee(const string& n,Date dd,int d)

             :name(n), hiring_date(dd), department(d) { }

Manager::Manager(const string& n,Date dd,int d,int lvl)

             : Employee(n,dd,d), level(lvl){ }

·拷贝构造函数 

派生类永远不会继承赋值运算符和复制构造函数。您应该自己定义复制派生类的语义。 如果将派生类对象复制到基对象中,则会出现切片问题。 使用基指针或基引用可以避免切片问题。

 void f(const Manager& m)

 {

    Employee e{m};   // slicing

    e = m;                  // slicing

 }

/* level and group members are ignored */

如何避免slicing problem?

Employee* or Employee&

给定一个 Base*,指向的对象真正属于哪个派生类型?

•Solutions:

  1. limit that only objects of a single type are

      pointed to. ( homogeneous objects )

  2. place a type tag and set it in every object. (heterogeneous objects)

  3. use virtual functions. (heterogeneous objects)

异构对象无法处理,使用类型字段是一种容易出错的技术,会导致维护问题,所以最好的办法是使用虚拟函数

虚拟函数允许程序员在基类中声明函数,这些函数可以在每个派生类中重新定义。 编译器和链接器将保证对象与应用于它们的函数之间的正确对应关系。 实现此任务的技术称为动态绑定或运行时绑定。

实现代码:

 class Employee

 { string name;

   Date hiring_date;

   short department;

   …

 public:

   virtual void print() const;

   string get_name() const

   { return name; }

   …

 };

 class Manager:public Employee

 {  list<Employee*> group;

    short level;

    …

  public:

        void print() const;

    /* virtual can be omitted here.*/

    …

 };

  void display_list(const list<Employee*>& s)

    {

        for (auto p : s)  p->print();

    }

 /* each object will be displayed according to its own type.             

  */           

这里引出了一个新的概念 Polymorphism(多态性)!

从基类的函数中获取正确的行为,而与实际使用的对象类型无关,这称为多态性。 具有虚函数的类型称为多态类型。 若要获得多态行为,调用的成员函数必须是虚拟的,并且必须通过指针或引用操作对象。

给一个例子:

 void f(const Manager& m, const Employee& ee)
 {     Employee e{m}; 
       e.print();                     // Employee::print
       const Employee* pe{&m}; 
       pe->print();              // Manager::print
      pe = &ee; 
       pe->print();             // Employee::print
      const Employee& re = m; 
      re.print();             // Manager::print
 } 

虚函数表

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

NightHacker

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

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

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

打赏作者

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

抵扣说明:

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

余额充值