5.4 物件的效能(Object Efficiency)
以下的效率測試中,物件構造和拷貝所需的成本是以Point3d class宣告為基礎,從簡單形式逐漸到複雜形式,包括Plain OI' Data、抽象資料型別(ADT)、單一繼承、多重繼承、虛擬繼承。以下是測試的主角:
Point3d lots_of_copies(Point3d a,Point3d b) { Point3d pC = a; pC = b;//(1) b = a; //(2) return pC; } main() { Point3d pA(1.725,0.875,0.478); Point3d pB(0.315,0.317,0.838); Point3d pc; for(int iters = 0; iters < 10000000;iters++) pC = lost_of_copies(pA,pB); }
最初的兩個程式中,一個是struct和一個public資料的class,對pA和平B的初始化操作explicit initialization list進行的:
struct Point3d{float x,float y,float z;};
class Point3d{public: float x,float y,float z;};
Point3d pA = {1.725,0.875,0.478};
Point3d pB = {0.315,0.317,0.838};
這兩個操作表現出bitwise copy語意,所以應該會預期它們的執行效率最好:
下一個測試,唯一的改變是資料的封裝以及inline函式使用,以及一個inline constructor,用以初始化每一個object。class仍然表現bitwise copy語意,所以執行效率應該相同,事實上則有些差距:
效率上的差異並不是因為lots_of_copies(), 而是因為main()函式中的class object的初始化操作。修改struct的初始化操作如下,並複製inline class constructor的擴充套件部分:
main()
{
Point3d pA;
pA.x = 1.725;pA.y = 0.875;pA.z = 0.478;
Point3d pB;
pB.x = 0.315;pB.y = 0.317;pB.z = 0.838;
//其餘相同
}
它們現在封裝後的class表現方式,時間都增加了:
經由constructor的inline expansion(擴充),座標成員的初始化操作帶來以下兩個指令的彙編碼:一個指令將常量載入到暫存器,另一個指令做真正的儲存操作;座標成員經由explicit initialization list來做初始化操作,會得到單一指令,因為常量值已經被預先載入好了。
封裝和為封裝的兩個Pointd3d宣告之間,另一個差異就是:
Point3d pc;
如果使用ADT表現法,pC會以其default constructor的inline expansion自動進行初始化——甚至在此例而言,沒有初始化也很安全。即使加上了封裝,還是完全相當於C程式中的直接儲存資料。
下一個測試,把Pointd3d分割成三個層次的單一繼承:
class Point1d{}; //_x
class Point2d : public Point1d{}; //_y
class Point3d : public Point2d{}; //_z
沒有任何virtual functions。由於Point3d仍展現出bitwise copy語意,所以額外的單一繼承不應該影響“memberwise物件初始化或拷貝操作“的成本:
下面多重繼承設計:
class Point1d{}; //_x
class Point2d{}; //_y
class Point3d : public Point1d,public Point2d{};//_z
由於Pointd class 仍然顯示出bitwise copy語意,所以額外的多重繼承關係不應該再memberwise的物件初始化操作或拷貝上增加成本:
截止目前的所有測試,所有版本的差異都是以“初始化三個local objects”為中心,而不是以“memberwise initialization和copy的損耗” 為中心。這些操作的完成都是一成不變的,都支援“bitwise copy”語意。但是匯入虛擬繼承,情況就改變了,下嗎是單層虛擬繼承:
class Point1d{};
class Point2d : public virtual Point1d{...};
class Point3d : public Point2d{...};
不再允許class有用bitwise copy語意(第一次虛擬繼承不允許,第二層繼承更加複雜)。合成的inline copy constructor和copy assignment operator派上用場,導致效率成本重大增加:
如果在前面那種(ADT):有著封裝並加上一個virtual function的class宣告。這種情況下不允許 bitwise copy語意,合成的inline copy constructor和copy assignment operator被產生出來,效率成本的增加不如預期,但比起bitwise copy卻也有大約40%~50%。如果這個函式是程式設計師提供的一個non-inline函式,成本更高:
下面測試採用其他有著bitwise copy語意的表現方式,取代inline合成的memberwise copy constructor和copy assignment operator。這一次反映出來是物件構造和拷貝的成本,原因是繼承體系的複雜度增加了: