Sunday, February 12, 2012

繼承(Inheritance)

在物件導向的程式語言中含有「繼承」的概念,最主要的效果為避免撰寫重複的程式碼。操作上,我們讓某個class所定義的object繼承另一個class,前者稱為base classes,後者稱為derived classes(C++語言中的叫法),derived classes可以繼承base classes裡面的屬性(attributes)和行為(behavior),換句話說,繼承的classes可以直接使用被繼承classes中的變數和函式,達到避免重複撰寫相同程式碼的效果。

接下來討論兩件事情:

  1. 繼承的方式:public, private, protected
  2. base classes中的不同類型(public, private ,protected)變數可以被derived classes使用的權限
繼承的方式:public, private, protected

最常用的是public繼承,他建立了derived classes和base classes的「is-a relationship」,例如:derived classes是car,base classes是vehicle,即car "is-a" vehicle。

定義derived classes時候,程式碼這麼寫:
class Car : public Vehicle{};


base classes中的不同類型(public, private ,protected)變數可以被derived classes使用的權限

在public形式繼承中,base classes的public變數可以被derived classes使用,private則不行,pretected變數則是設計給繼承使用的,可以被derived classes使用。

在OOP概念中,希望東西一層層分離、封裝,透過給予使用介面(interface)的方式連結,而不希望使用者(這裡指derived classes)去更變內部的資料,於是,上述的public, protected變數可以直接被derived classes使用就不會是一個好的方法,最好的方式是透過private變數再使用public的set/get函式去使用變數

透過private變數再使用public的set/get函式去使用變數,帶來兩個最明顯的好處:

  1. set的函式中往往會檢查輸入值是否為有效值,或是一些客製化的處理,若是繞過set/get函式去更變變數,可能導致該變數儲存了無效值
  2. 若是base classes改變內部的設置(程式碼),直接讀取、使用base classes的public, protected變數的方式,derived classes的程式碼也可能要跟著更變,導致整份程式變得複雜麻煩,而使用set/get的方式可以避免這樣的問題,即是「封裝」的概念
一些事情
  • base classes的程式碼是不需要因為繼承而有任何更改的
  • derived classes程式碼中要include base classes存放的header檔
  • derived classes程式碼中要使用base classes中的變數或函式可以直接使用,但若是該變數或函式已經被另一個相同名稱的變數或函式覆蓋,可以在前面加上"base clesses name::"的方式讀取使用,例如:Vehicle::drive()
  • 若是base classes已經有的東西盡量避免重複撰寫,善用base classes提供的函式

Example Code
以下範例是用Student當作base class,每個Student object有自己的名字、分數,然後有一個叫做GodStudent的class當作derived class,繼承Student,GodStudent "is-a" Student,但是GodStudent與其他Student不同的是,他對於自己有一個最低標準分數(baseScore)。


base class的header (Student.h)
#include <string>
using namespace std;

class Student{
 public:
  Student(const string &, int = 0);

  void setName(const string &);
  string getName() const;

  void setScore(int);
  int getScore() const;

  void print() const;

 private:
  string name;
  int score;
};


base class的implementation (Student.cpp)
#include <iostream>
#include <string>
#include "Student.h"

using namespace std;

Student::Student(const string &_name, int _score){
 name = _name;
 score = _score;
}

// name, set/get
void Student::setName(const string &_name){
 name = _name;
}

string Student::getName() const{
 return name;
}

// score, set/get
void Student::setScore(int _score){
 score = _score;
}

int Student::getScore() const{
 return score;
}

// print
void Student::print() const{
 cout << "Name >> " << name << "\tScore >> " << score << endl;
}


derived class的header (GodStudent.h)
#include <string>
#include "Student.h"
using namespace std;

class GodStudent : public Student{
 public:
  GodStudent(const string &, int = 0, int = 90);

  void setBaseScore(int);
  int getBaseScore() const;

  void print() const;
 private:
  int baseScore;
};


derived class的implementation (GodStudent.cpp)
#include <iostream>
#include "GodStudent.h"

using namespace std;

GodStudent::GodStudent(const string &_name, int _score, int _baseScore)
 :Student(_name, _score){
 setBaseScore(_baseScore);
}

void GodStudent::setBaseScore(int _baseScore){
 baseScore = _baseScore;
}

int GodStudent::getBaseScore() const{
 return baseScore;
}

void GodStudent::print() const{
 // accessing protected variables
 /*
 cout << "Protected Variables" << endl;
 cout << "Name >> " << name << "\tScore >> " << score;
 cout << "\tBaseScore >> " << baseScore << endl;
 */

 // accessing private variables
 cout << "\nPrivate Variables" << endl;
 Student::print();
 cout << "BaseScore >> " << baseScore << endl;
}


使用者的主程式 (main.cpp)
#include <iostream>
#include <iomanip>
#include "GodStudent.h"

using namespace std;

int main(){
 GodStudent heron("Heron", 100, 90);

 cout << "getName >> " << heron.getName() << endl;
 cout << "getScore >> " << heron.getScore() << endl;
 cout << "getBaseScore >> " << heron.getBaseScore() << endl;

 heron.setScore(120);
 heron.print();

 return 0;
}