C++ 性能剖析 (二):值語義 (value semantics)


 

Value Semantics (值語義) 是C++的一個有趣的話題。

 

什么是值語義? 簡單的說,所有的原始變量(primitive variables)都具有value semantics. 也可以說,它們可以對應傳統數學中的變量。有人也稱它為POD (plain old data), 也就是舊時的老數據(有和 OOP 的新型抽象數據對比之意)。

 

對一個具有值語義的原始變量變量賦值可以轉換成內存的bit-wise-copy。

 

對於用戶定義的類呢?我們說,如果一個type X 具有值語義, 則:

1)X 的size在編譯時可以確定。

     這一點看似自然,其實在C++里有許多變量的size編譯時無法確定。比如我在reference 三位一體里提到的polymorphic 變量,因為是“多身份”的,其(內容)的size是動態的。

2)將X的變量x,賦值與另一個變量y,無須專門的 = operator,簡單的bit-wise-copy 即可。

3)當上述賦值發生后,x和y脫離關系:x 和 y 可以獨立銷毀, 其內存也可以獨立釋放。

 

了解第三點很重要,比如下面的class A就不具備值語義:

class A

{

     char * p;

     public:

          A() { p = new char[10]; }

          ~A() { delete [] p; }

};

A 滿足1和2,但不滿足3。因為下面的程序會出錯誤: 

Foo()

{

    A a;

    A b = a;

} // crash here

 

改進的方法是定義一個A::operator=(constA &),並且用reference counting 的技術來保護指針,實現起來並不簡單。所以我們說一旦一個class 失去了value semantics, 它也就失去了簡單明了的 = 語義。

 

從上面的分析可以得出結論,value semantics 有個簡單的 = , 也正是數學意義上的 =

 

學過Java, C#, 和JavaScript的程序員都知道,這些語言里的object都不具有值語義,因為它們都是指針,= 並不copy內容。也不滿足條件3。

 

那么value semantics 對C++性能有什么影響呢?我覺得有以下幾方面:

 

1)std 庫是基於值語義 的。std container 包含的元素,都具有值語義. 不理解這一點,就不能正確使用std,也不會對std的性能,做出合理預期。

2)簡單的bit-wise-copy 賦值語句一般會提高賦值性能,因為它不需要特殊的 = operator 了。在使用std container 時,會有大量的copy 或assignment。 bit-wise-copy對於小的變量通常比函數划算得多。

3)具有值語義的,size 不大的變量,在stack里,作為auto變量,傳遞,拷貝,釋放全部和原始變量的用法完全一致,既好用,一般也具有優良的性能。動態語言缺乏這個(值語義)的語言構造和能力,(C# 有有限地支持:c# struct),所以速度上很難優化。

4)注意,在設計具有值語義的類時,不要保留無用的destructor. destructor 的存在,使得你的類的語義和原始類有了本質的區別,C++ 編譯會為此處心積慮地添加管理代碼,使得一個簡單的函數復雜化(會專門著文論證), 並且嚴重影響性能。這些當然是有附加值的,但是必須是設計需求的,而不是簡單照搬的。

5)不要保留無用的destructor這點,對使用 std 時更重要。我會專門論述。不要有臃余無用的 destructor 對任何類都是適用的,而對於有大量 copy, assignment 的 std container 尤為重要!

 

那么,什么樣的類沒有值語義呢?我們不妨稱這種型為 none-value-semantics type (NVST).

 

1)有virtual function 的類

2)包含NVST成員的類

3)NVST 的衍生類(derived classed)

4)定義了自己的 = operator 的類

5)繼承 virtual 基類的衍生類

 

2014-6-21 西雅圖


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM