文章目錄
條款26:儘可能延後變量定義式的出現時間
std::string encryptPassword(const std::string& password)
{
using namespace std;
string encrypted;
if (password.length() < MinimumPasswordLength) {
throw logic_error("Password is too short");
}
...
return encrypted;
}
上述這個案例中encrypted對象沒有被使用,但是經歷了一次構造和析構過程。將其延後到異常拋出之後,就可以避免這種問題。
如條款4解釋“通過default構造函數構造出對象後再對它賦值比直接在構造時指定初值效率更差”,不應該只是延後變量的定義,甚至延後到能夠給它初值爲止。從而避免毫無意義的default構造行爲。
條款27:儘量少做轉型動作
C++提供了四種新式轉型:
1、const_cast:將對象的常量性移除。
2、dynamic_cast:用於“安全向下轉型”,決定某對象是否歸屬體系中的某個類型。(效率低)
3、reinterpret_cast:爲操作數的位模式提供較低層次的重新解釋。例如將pointer to int轉爲int。
4、static_cast:強迫隱式轉換。
轉型的缺陷:
(1) 任何類型轉換都會令編譯器編譯出運行期間執行的代碼。
int x, y;`在這裏插入代碼片`
double d = static_cast<double>(x) / y; // x轉型爲double會產生一些代碼,因爲底層實現中int不同於double
(2) 單一對象可能擁有一個以上的地址。
class Base {...};
class Derived: public Base {...};
Derived d;
Base* pb = &d; // pb指向&d + 偏移量
Derived *pb1 = &d; // pb1指向&d
(3) 轉型可能導致代碼似是而非
class Derived: public Base {
public:
virtual void onResize() {
static_cast<Base>(*this).onResize(); // static_cast<Base>(*this)返回*this的副本
...
}
};
(4) dynamic_cast效率很低:每次使用都會進行class名稱比較。
條款28:避免返回handles指向對象內部成分
一旦handle被傳出去了,就意味着存在“handle比其所指對象更長壽”的風險。
Class GUIObject {...};
const Rectangle boundingBox(const GUIObject &obj);
GUIObject *pgo;
const Pointer* p = &(boundingBox(*pgo).upperLeft());
// 上述boundingBox()返回一個臨時的Rectangle對象,當這樣語句結束時該對象就會被銷燬,但是upperLeft()返回的handle還在。
條款29:爲“異常安全”而努力是值得的
具備異常安全性的函數在當異常被拋出時會:
(1) 不泄露任何資源。
(2) 不允許數據被破壞。
這樣的函數區分三種級別的保證:
(1) 基本型:異常被拋出,程序內任何事物仍然保持在有效狀態。
(2) 強烈型:異常被拋出,程序狀態不會改變,即函數成功,就是完全成功;函數失敗,就會恢復到“調用函數之前”的狀態。
(3) 不拋異常型:承諾絕不拋異常。
條款30:透徹瞭解inlining的裏裏外外
inline函數背後的整體觀念是,將“對此函數的每一個調用”都以函數本體替換之。將大多數inline限制在小型、被頻繁調用的函數身上,可使日後的調試過程和二進制升級更容易,也可以使代碼膨脹最小化。