《Effective C++》的閱讀進度依然過半,難度當然也逐漸提升。於是每個章節結束,就來個小考題複習一下吧!
Q1. 根據知識點 26 的建議,以下程式碼有什麼效率上的問題?
std::string encryptPassword(const std::string& password)
{
using namespace std;
string encrypted;
if(password.length() < MinimumPasswordLength)
{
throw logic_error ("Password is too short");
}
// encryption logic here...
return encrypted;
}
A) 該函式的回傳值未使用 reference。
B) 例外 exception 的時機過晚。
C) 變數 encrypted 並未初始化值。
D) 該函式不應使用 using namespace std;。
E) 變數 encrypted 過早被定義。
此程式碼中,變數
encrypted在尚未確認需要之前就被定義,但實際上只有在驗證密碼長度後才會被使用。如果在檢查密碼長度時拋出了 exception,那encrypted的建構和解構都變得完全沒有意義。
若能將 encrypted 的定義延後執行,能有效避免額外的建構與資源浪費。透過這種調整,可以提升程式的效率,避免不必要的資源建構與釋放。
Q2. 舊式轉型與 static_cast, dynamic_cast 等 C++ 型別轉換相比,下列敘述何者正確?
A) 舊式轉型可強制類型轉換,總是比 C++ 型別轉換更安全。
B) C++ 型別轉換在程式碼中更容易辨識,並能讓編譯器檢測出更多錯誤情境。
C) 在程式碼的可讀性上,C++ 型別轉換與舊式轉型沒有任何區別。
D) 使用 C++ 常見的 4 種轉型類型時,皆能移除 const 限制。
E) 舊式轉型更受偏好,因為它們更具明確性。
C++ 型別轉換在程式碼中更容易辨認,同時每種轉型有明確且限定的用途,讓編譯器可以更有效地檢查錯誤。
C++ 型別轉換的優勢在於明確性與安全性,透過清楚的轉型目標和編譯器的協助,能顯著降低程式中的潛在異常。例如:只有const_cast 可以移除 const 限制;若以 static_cast 嘗試進行無效的轉型,則編譯階段會觸發錯誤。
相比之下,(type)object 舊式轉型較不具明確性,容易隱藏微妙的錯誤因而更危險。尤其在處理複雜類型時可能導致意想不到的行為,因此舊式轉型在現代 C++ 中較少被推薦。
Q3. 以下是 YoyoPicture::changeBackground 的實作範例:
void YoyoPicture::changeBackground(std::istream& imgSrc)
{
lock(&mutex);
delete bgImage;
++imageChanges;
bgImage = new Image(imgSrc)
unlock(&mutex);
}
以 exception-safe 的角度來看,該函式實作中最大的弱點是什麼?
A) 程式碼過於冗長,可讀性較低。
B) 某些狀況下可能會導致資料結構損毀。
C) bgImage 並未使用智慧指標。
D) mutex 應宣告為 class 的成員變數。
E) 在刪除舊圖像前就遞增了 imageChanges 計數。
如果
new Image(imgSrc)發生異常,mutex可能無法執行unlock,導致鎖資源無法釋放。而當bgImage已被刪除,但此時若未完成新圖像的建構,bgImage會變成一個dangling pointer ,可能指向已經不存在的記憶體。
如果發生 exception,函式應保證至少滿足基本的安全性 —— 物件保持一致性且沒有資源洩漏。可使用 知識點 13 開始不斷提到的 RAII 模式和智慧指標來管理資源。例如用 std::unique_ptr 或 std::shared_ptr 自動管理記憶體釋放,並搭配 std::lock_guard 或 std::unique_lock 等功能的智慧鎖。
改進後的程式碼範例如下:
void YoyoPicture::changeBackground(std::istream& imgSrc)
{
std::lock_guard<std::mutex> guard(mutex); // 自動管理鎖
std::unique_ptr<Image> newImage(new Image(imgSrc)); // 智慧指標管理記憶體
bgImage.swap(newImage); // 更新圖像指標
++imageChanges; // 更新計數器
}
Q4. 以下關於 C++ 中的 pimpl 技巧,哪一項不是它的優點?
A) 通過隱藏實現細節,減少編譯依賴。
B) 能修改 implementation,而無需重新編譯客戶端程式。
C) 能真正分離 interface 及 implementation。
D) 消除與動態記憶體分配相關的所有執行期負擔。
E) 避免客戶端依賴 implementation 細節。
pimpl 能有效減少編譯依賴,實現 interface 與 implementation 的分離。其避免客戶端對實現細節的依賴,在提升封裝性方面十分有效。然而它並不能消除執行期的負擔。
事實上正好相反,pimpl 會帶來一些額外的效能負擔。
new 分配記憶體來管理 implementation。