C++ 語意上用 virtual 區分 base class 希望 derived class 自己定義與不希望 derived class 修改的 function,virtual 為希望 derived class 自行定義,non-virtual 則不希望 derived class 修改。
C++ 在區分 interface 及 implement 繼承上有:
pure virtual function
impure virtual function
non-virtual function
pure virtual function
pure virtual function 是為了讓 derived class 繼承 base class 的 interface,而 implementation 則由 derived class 全權處理。當一 class 宣告 pure virtual function 時,代表要求 derived class 必須 implement 這些 pure virtual function(沒寫會 compile error),通常是這個繼承體系所需要的共同性質。
impure virtual function
impure virtual function 是為了讓 derived class 繼承 interface 及預設的 implementation。也就是說,derived class 可以自己實作,也可以直接用 base class 的實作。
這有個風險是,因為在 derived class 中並不強迫要寫出 base class 的 impure virtual function,導致在 derived class 應該有自己實作的狀況下,programmer 可能忘記在 derived class 實作。
non-virtual function
non-virtual function 主要是為了讓 derived class 繼承 base class 的實作。
雖然語法上 derived class 可 override base class 的 non-virtual function,但語意上的設計不建議 derived class override base class 的 non-virtual function。另外,override virtual function 跟 override non-virtual function 在實際執行上會有不同行為。
non-virtua function 是 static binding。在一個繼承體系中 call non-virtual function 時會直接 call 宣告 type 的 class function。如果 derived class 沒有 override function,則因繼承關係 call 到 base class 的 function。如果 dervied class override function,則會 call 到 derived class 的。
Ref
《Effective C++》Differentiate between inheritance of interface and inheritance of implementation.
construct 到 base class 時,C++ 視該 object 為 base class object,derived class 的 virtual function 跟 member 都還沒建出來,所以 call 不到 derived class 的 virtual function。
反過來的 destruct 則是在 derived class destructor 跑完後,derived class 的東西都刪光光了,該 object 就被當作 base class object,所以在 base class destructor 裡 call virtual function 不會 call 到 derived class 的 virtual function。
不能修改 class non-static member data 也不能 call 其他非 const member function 的 member function。
宣告 function 時在最後面加個 const 的 function,ex:
1
intGetIndex()const;
Declaring a member function with the const keyword specifies that the function is a “read-only” function that does not modify the object for which it is called. A constant member function cannot modify any non-static data members or call any member functions that aren’t constant.
compiler 會檢查 const member function 是否有改到 member data、return value 是否為 pointer 或 reference,如果改到 member data 或傳回可以讓外界修改 member data 時 compiler 會 error。可以透過這個特性反向檢查一個原本非 const 的 member function 是否有修改到 member data。
如果要在 const member function 中修改特定 member data,可以在該 member data 的宣告前面加上 mutable。
PHPUnit 在一些系統上會有 header already sent 的錯誤,加 --stderr 可以避免 PHPUnit 把這視為測試錯誤。
執行單一測試,指定某測試檔做測試。
1
$ phpunit --stderr <test file path>
Basic conventions
class “Class” 的 test 放在 class “ClassTest”
ClassTest 要繼承 PHPUnit_Framework_TestCase
test 為 public method,method name 為 test*
在 test 中,用 assert*() function check value 是否符合預期
failure vs error
failure 是 assertion function fail(要測試的 function 的 output 不符合預期)。以 F 代表。
error 是出現 unexpected exception 或 PHP error。以 E 代表。
setUp() & tearDown()
為了讓測試是正確且獨立的,要保持執行 test 時環境是乾淨的。在每次執行一個 test method 前會 call setUp() 建立 test 所需要的 object、變數等等。run 完 test method 後會 call tearDown() 清東西。
通常只需 implement tearDown(),用來 close file、socket 等。
用 file system 組合 test suite
可以用 file system 的 directory structure 達到組合出一個 test suite 的目的。這也是最簡單的方式。:)
建立一 test dir 並建立跟 project dir 中一樣的 directory structure。把 test case source file(就是 test method 的 source code)以相應 project source file 的位置放進去。PHPUnit 會自動 traverse test dir 做 test。
舉例說明:
這是 project directory:
1 2
object/ MyStack.php
這是 test directory:
1 2
tests/ MyStackTest.php
PHPUnit 會在 test dir(上例的 tests/)底下找 *Test.php 來做測試。
執行 test suite
1
$ phpunit --stderr <test dir path>
寫 test 就是…
生一些 testcase(input 及預期的 output)
在 test method 裡 call 要測的 function,把 test case 的 input 當作 arg 丟進去。
A condition (short for ``condition variable’’) is a synchronization device that allows threads to suspend execution and relinquish the processors until some predicate on shared data is satisfied. The basic operations on conditions are: signal the condition (when the predicate becomes true), and wait for the condition, suspending the thread execution until another thread signals the condition.
A condition variable must always be associated with a mutex, to avoid the race condition where a thread prepares to wait on a condition variable and another thread signals the condition just before the first thread actually waits on it.
condition variable 必須搭配 mutex,避免 race condition:thread A 在 thread B signal condition 後才 wait 這個 condition variable,這種狀況可能讓 thread A 永遠在 sleep(如果沒有人會再 signal 這個 condition variable)。
為有「多型」性質(即有 virtual function)的 base class 宣告 virtual destructor。
C++ 沒有定義 base class 的 destructor 是 non-virtual 時以 base class pointer 刪除 derived class 的 object 的行為。常出現的結果是只有 base class 部分的 memory 會被釋放、derived class 部分的不會釋放,這會造成「局部銷毀」物件的問題,memory 當然就沒 free 乾淨。
用 virtual destructor 可以解決上述問題,因為 virtual function 的性質會 call 到 derived class 的 destructor。
不過不是把所有 base class 的 destructor 宣告成 virtual 就好,非必要(沒有 virtual function)時不需要這麼宣告。