iT邦幫忙

2021 iThome 鐵人賽

DAY 25
0

在寫程式時,若需定義多個類別(比如類別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;


上一篇
Day24
下一篇
Day26
系列文
少年DevOps的C++奇怪漂流30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言