Day25

在写程序时,若需定义多个类别(比如类别A、B、C),而类别B、C拥有类别A的某些资料成员、或某些成员方法,则我们可以使用继承让B/C直接获得A的public与protect部分,在C++中一般称呼A为基本类别,B与C为衍生类别,基本类别就是Java里的父类别而衍生类别就是子类别,C++真是一个麻烦的语言,在C++中类别分成public, protect, private三部分,继承也分为public, protect, private inheritance,我们主要探讨蚂蚁书的public inheritance中关於私有资料成员如何处理的范例:

https://ithelp.ithome.com.tw/upload/images/20211007/20098886BeuEXERBdr.png

如上图所示即使是public inheritance也无法存取父类别的private member(date and method),下面是父类别Employee的标头档可以看到有三个private date member,分别是firstName, lastName, socialSecurityNumber, 子类别是无法读取的

本日篇幅较多包含10个档案所以所有程序码都摆在day25_example

// Fig. 13.13: Employee.h
// Employee abstract base class.
#ifndef EMPLOYEE_H
#define EMPLOYEE_H

#include <string> // C++ standard string class
using std::string;

class Employee 
{
public:
   Employee( const string &, const string &, const string & );

   void setFirstName( const string & ); // set first name
   string getFirstName() const; // return first name

   void setLastName( const string & ); // set last name
   string getLastName() const; // return last name

   void setSocialSecurityNumber( const string & ); // set SSN
   string getSocialSecurityNumber() const; // return SSN

   // pure virtual function makes Employee abstract base class
   virtual double earnings() const = 0; // pure virtual
   virtual void print() const; // virtual
private:
   string firstName;
   string lastName;
   string socialSecurityNumber;
}; // end class Employee

#endif // EMPLOYEE_H

类别CommissionEmployee的原始码.cpp档中可以发现子类别的print()方法中有一段呼叫父类别方法的代码Employee::print();,再去点开父类别的原始码.cpp档会发现父类别的print()方法会再call三个成员方法getFirstName(),getLastName(), getSocialSecurityNumber()。

// Fig. 13.14: Employee.cpp
// Abstract-base-class Employee member-function definitions.
// Note: No definitions are given for pure virtual functions.
#include <iostream>
using std::cout;

#include "Employee.h" // Employee class definition

// constructor
Employee::Employee( const string &first, const string &last,
   const string &ssn )
   : firstName( first ), lastName( last ), socialSecurityNumber( ssn )
{
   // empty body 
} // end Employee constructor

// set first name
void Employee::setFirstName( const string &first ) 
{ 
   firstName = first;  
} // end function setFirstName

// return first name
string Employee::getFirstName() const 
{ 
   return firstName;  
} // end function getFirstName

// set last name
void Employee::setLastName( const string &last )
{
   lastName = last;   
} // end function setLastName

// return last name
string Employee::getLastName() const
{
   return lastName;   
} // end function getLastName

// set social security number
void Employee::setSocialSecurityNumber( const string &ssn )
{
   socialSecurityNumber = ssn; // should validate
} // end function setSocialSecurityNumber

// return social security number
string Employee::getSocialSecurityNumber() const
{
   return socialSecurityNumber;   
} // end function getSocialSecurityNumber

// print Employee's information
void Employee::print() const
{ 
   cout << getFirstName() << ' ' << getLastName() 
      << "\nsocial security number: " << getSocialSecurityNumber(); 
} // end function print

在CommissionEmployee的标头档可以发现确实没有定义firstName, lastName, socialSecurityNumber三个资料成员与getFirstName(),getLastName(), getSocialSecurityNumber()三个成员方法,现在好玩的来了!如果再多一个子类别:佣金+底薪员工去继承佣金员工CommissionEmployee会怎样呢? 老样子直接点开BasePlusCommissionEmployee的标头档与与原始码.cpp,可以惊人的发现BasePlusCommissionEmployee的成员方法print()直接呼叫CommissionEmployee::print();也没有去定义所需的成员方法与资料。这就是继承的核心大量的程序码复用reuse。

最後点开主程序fig13_25.cpp,可以看出父类别Employee有三个子类别SalariedEmployee, HourlyEmployee, CommissionEmployee与一个孙类别BasePlusCommissionEmployee,首先创造一个Employee类别指标向量vector < Employee * > employees( 4 ); 由於四个类别都继承自Employee所以可以直接摆到Employee类别指标向量中,接着透过for回圈逐一呼叫print()方法成员,这里有个Tricky,因为我们前面使用的是指标所以是必须使用->如employees[i]->print();而非.(dot) 呼叫成员方法,而回到typeid时就不再是指标所以用.(dot) 如typeid( *employees[j] ).name() << endl;


<<:  #21-用Canvas做科技感的动态球!(+什麽时候该用CSS/SVG/Canvas?)

>>:  焦虑与压力

Day 10 Swift语法-进阶篇(3/5)-Initialization

我们在定义类别或结构时,有时候会需要做初始化的动作,简单说,就是给一个值,譬如我们在写C的时候,如果...

ASP.NET MVC 从入门到放弃(Day10) -C# get set 自动属性介绍

接着来讲讲get set部分.... public class A { public string ...

学习Python纪录Day28 - 在多文字档中搜寻关键字

在多文字档中搜寻关键字 第一层for回圈使用了os.walk()递回取得路径下的所有档案 第二层fo...

[Day29] 後端13:後端Code总结

不过有些需要下的指令可不能省喔~ .env APP_NAME=Laravel APP_ENV=loc...

p段落标签-基础用法

p段落标签最常使用搭配段落文章使用 同时也是一个display:block特性的元素 <p&g...