Saturday, February 18, 2012

templates(模板)

templates採通用格式的方法,在C++語言中可大量減少撰寫重複的程式碼,分為兩種形式:

  • function templates
  • class templates

Function Templates

類似於overload functions

  • overload functions用於相似或是相同的情況
  • function templates則用於完全相同而只有資料型態差異的情況下
definition(出現在該function前面):
  • template< typename T >
  • template< class ElementType >
在<>符號中間的為template parameters,超過一個用逗號分開,型態有class或typename。

#include <iostream>
using namespace std;

// function template
template< typename T>
void printArray(const T * const array, int size){
 for(int i=0 ; i<size ; i++){
  cout << array[i] << " ";
 }
 cout << endl;
}

int main(){

 int a[4] = {1,2,4,3};
 char b[3] = {'b', 'c', 'a'};
 double c[5] = {1.23, 12.3, 0.12, 1.233, 0.11};

 printArray(a, 4);
 printArray(b, 3);
 printArray(c, 5);

 return 0;
}


範例中,當int a[]傳進來的時候T就會變成int,char進來則是char,以此類推。

"The compiler uses its overload resolution capabilities to find a definition of function printArray the best matched the function call."
compiler使用類似於overload的解法處理printArray中的T,會自動找尋最合適的function call。
如此可以避免寫重複的程式碼,產生一些「通用」的function。

注意:

  • 若是自訂的資料型態,要記得有沒有先overload該型態的operators(for example, ==, >=, ...)
  • 同一個template definition裡頭names of template parameters不能重複;不同template definition則可以重複
  • template的處理在compile time

Class Templates

另一種template為class templates,加在class definition的前面:欲達到的效果和function templates一樣,希望可以產生一個「通用」的方式,避免撰寫重複的程式碼。


Stack.h
#include <iostream>

using namespace std;

template<typename T>
class Stack{
 public:
  Stack(int = 10); // default constructor
  ~Stack(){ // destructor
   delete [] stackPtr;
  }
  bool push(const T &);
  bool pop(T &);

  bool isEmpty() const{
   return top == - 1;
  }
  bool isFull() const{
   return top == size - 1;
  }

 private:
  int size;
  int top;
  T *stackPtr;
};

template<typename T>
Stack< T >::Stack(int s):
 size(s>0 ? s : 10),
 top(-1),
 stackPtr( new T[size] ){
}

template<typename T>
bool Stack< T >::push(const T &pushValue){
 if(!isFull()){
  stackPtr[++top] = pushValue;
  return true;
 }
 return false;
}

template<typename T>
bool Stack<T>::pop(T &popValue){
 if(!isEmpty()){
  popValue = stackPtr[top--];
  return true;
 }
 return false;
}

main.cpp
#include <iostream>
#include "Stack.h"
using namespace std;

int main(){

 /* int */
 Stack<int> intStack(5);
 int intValue = 1;

 // push
 while(intStack.push(intValue)){
  cout << intValue << " ";
  intValue += 2;
 }
 cout << endl;

 // pop
 while(intStack.pop(intValue)){
  cout << intValue << " ";
 }
 cout << endl;


 /* double */
 Stack<double> doubleStack(6);
 double doubleValue = 1.0;

 // push
 while(doubleStack.push(doubleValue)){
  cout << doubleValue << " ";
  doubleValue *= 1.1;
 }
 cout << endl;

 // pop
 while(doubleStack.pop(doubleValue)){
  cout << doubleValue << " ";
 }
 cout << endl;

 return 0;
}

另外,兩件事情值得一提:

其一non-type template parameters,我們可以透過以下的方式使用template parameters達到一般argument的效果,其中template definition:
template<typename T, int elements>
使用:
Stack<int, 13> helloStack;
T對應到int,elements對應到13

其二,default type,當使用時沒有給予資料型態時賦予,則預設資料型態,用以下方法定義:
template<typename T = int>
當我們這麼使用時,T會變成預設的int:
Stack<> scores;


 

Friday, February 17, 2012

overload functions

C++語言中支持overload function的功能,即是同一個名稱的function可以定義很多次,每一次都可以有

  • 不同數量  和
  • 資料型態
  • 輸入參數
  • 傳回值
而使用函式時,compiler會找到最相近的函式定義去執行。

Wiki 中提供很易懂的C++例子:http://en.wikipedia.org/wiki/Function_overloading

Monday, February 13, 2012

Polymorphism

接續繼承(Inheritance),使用該文章的範例。

在繼承之後,各個derived class的object往往會需要執行同一個base class裡的函式,然而在不同的derived class裡,執行該函式的方法可能不同;讓base class的函式得以在不同derived class裡有不同的方式其實現則稱Polymorphism。

舉例,base class為「圖形」,derived class有「方形」、「圓形」,「圖形」這class裡有draw()這個函式,「方形」、「圓形」雖都繼承了這個函式,但是要draw出圖形的方法卻不同,這問題則是Polymorphism要討論。

首先我們看一下底下的程式碼(接續繼承(Inheritance)中的範例)

main.cpp

#include <iostream>
#include <iomanip>
#include "GodStudent.h"

using namespace std;

int main(){

 Student people("People", 60);
 Student *ppeople = &people;

 GodStudent heron("Heron", 100, 90);
 GodStudent *pheron = &heron;

 // base-class object
 people.print(); // static binding
 ppeople->print(); // dynamic binding

 // derive-class object
 heron.print(); // static binging
 pheron->print(); // dynamic binding

 //
 // pheron = &people; // this can not work
 ppeople = &heron;

 return 0;
}

這範例中有兩件事情要說明:

  1. base-class pointer可以指向derive-class object,但derive-class pointer不能指向base-class object。因為derive-class object "is-a" base-class object,所以第一句敘述是對的,C++中會依照宣告的資料型態去操作資料,所以透過base-class pointer指向derive-class object的方法只能使用base-class中有定義的函式,而derive-class中新定義的函式便沒有辦法操控(像是這例子的getBaseScore()/setBaseScore())
  2. object後面加"."讀取函式的方法為static binding,透過pointer/reference的方法為dynamic binding。static binding可以在compile的時候即知道有無錯誤,dynamic binding則要到程式執行時才能知道有沒有問題。
為了解決能讓base-class pointer指向derive-class object也能使用derive-class中的函式之問題,我們要介紹"virtual"。
  • virtual,在base class中在某函式定義前面加virtual,往後base-class pointer指向derive-class object時,仍可以呼叫到derive-class object的函式,例如print(),他即可呼叫derive-class的print(),得以印出baseScore。撰寫derive-class時,寫覆蓋過base-class中的virtual函式的動作稱為override。
  • pure virtual,此方法只在base class中寫該函式的interface(header中的prototype),不寫inplementation,且在prototype最後、分號前加"= 0"。此方法達到兩件事情:
    • 含有pure virtual函式的class稱為abstract class(反義為concrete class),只有抽象意義,不能產生instance。例如「二維」是abstract class,底下繼承的「方形」、「圓形」才是concrete class。
    • 在直接或間接的derive class中要能生成instance(concrete class),必須完成所有pure virtual函式的implementation
如此一來,在上層的base class中定義pure virtual函式,待不同的derive class來implement,得以解決本文最初提出的問題。同時也架構起了OOP。

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;
}