《C++应用程序性能优化::第二章C++语言特性的性能分析》学习和理解
《C++應(yīng)用程序性能優(yōu)化::第二章C++語言特性的性能分析》學(xué)習(xí)和理解
說明:《C++應(yīng)用程序性能優(yōu)化》 作者:馮宏華等 2007年版。最近出了新版,看了目錄,在前面增加了一章的內(nèi)容,其它的沒變。
知識(shí)點(diǎn):分析可能引起性能下降的幾個(gè)方面:構(gòu)造函數(shù)/析構(gòu)函數(shù),繼承與虛擬,臨時(shí)對(duì)象,內(nèi)聯(lián)函數(shù)
1、性能瓶頸
很多時(shí)候,一個(gè)程序的速度在框架設(shè)計(jì)完成時(shí)大致已經(jīng)確定了,而并非是因?yàn)椴捎昧薈++語言才使其速度沒有達(dá)到預(yù)期的目標(biāo)。當(dāng)遇到性能問題時(shí),首先應(yīng)該檢查和反思程序的總體框架。然后用性能檢測(cè)工具對(duì)其實(shí)際運(yùn)行做準(zhǔn)確地測(cè)量,再針對(duì)瓶頸進(jìn)行分析和優(yōu)化,這才是正確的思路。
確實(shí)有一些操作或者C++的一些語言特性比其它因素更容易成為程序的瓶頸,一般公認(rèn)的有如下因素:
(1)?????? 缺頁(yè)。缺頁(yè)往往意味著需要訪問外部存儲(chǔ)。因?yàn)橥獠看鎯?chǔ)訪問相對(duì)于訪問內(nèi)存或者代碼執(zhí)行,有數(shù)量級(jí)的差別。只要有可能,應(yīng)該盡量減少缺頁(yè)。
(2)?????? 從堆中動(dòng)態(tài)申請(qǐng)和釋放內(nèi)存:new/malloc,delete/free都是非常耗時(shí)的,因此應(yīng)該盡可能優(yōu)先考慮從線程棧中獲得內(nèi)存。優(yōu)先考慮棧而減少?gòu)膭?dòng)態(tài)堆中申請(qǐng)內(nèi)存,不止因?yàn)樵诙阎虚_辟內(nèi)存比棧慢,還因?yàn)橐M量減少缺頁(yè)。當(dāng)程序執(zhí)行時(shí),當(dāng)前??臻g所在的內(nèi)存頁(yè)肯定在物理內(nèi)存中,因此不會(huì)出現(xiàn)缺頁(yè);而訪問堆中的對(duì)象則可能會(huì)引起缺頁(yè)。
(3)?????? 復(fù)雜對(duì)象的創(chuàng)建和銷毀。這往往是一個(gè)層次相當(dāng)深的遞歸調(diào)用,可能會(huì)有臨時(shí)對(duì)象在不知不覺中產(chǎn)生。
(4)?????? 函數(shù)調(diào)用。函數(shù)調(diào)用有固定的額外開銷,當(dāng)函數(shù)體代碼量較少時(shí)應(yīng)該采用C++的內(nèi)聯(lián)函數(shù)。
2、構(gòu)造函數(shù)和析構(gòu)函數(shù)
創(chuàng)建一個(gè)對(duì)象一般有兩種式:一種是從線程運(yùn)行棧中創(chuàng)建,也稱為“局部對(duì)象”:ClassA obj;;一種是從全局堆中動(dòng)態(tài)創(chuàng)建:ClassA *p = new ClassA;…delete p;。
局部對(duì)象空間的分配在程序進(jìn)入該對(duì)象的作用域時(shí)就已經(jīng)分配好了,一般是通過移動(dòng)棧指針,當(dāng)執(zhí)行到ClassA obj;時(shí),只需要調(diào)用構(gòu)造函數(shù)。
動(dòng)態(tài)創(chuàng)建對(duì)象,執(zhí)行Class *p = new ClassA;時(shí)會(huì)從全局堆中獲取對(duì)象內(nèi)存空間,并將地址指針賦值該p。p是棧中的局部對(duì)象,p所指向的對(duì)象是全局堆中的空間。在全局堆中要銷毀對(duì)象必須顯式調(diào)用delete,delete p會(huì)調(diào)用p所指向?qū)ο蟮奈鰳?gòu)函數(shù),并將該對(duì)象所占的全局堆內(nèi)存返回給全局堆。
銷毀了對(duì)象之后,p指針在程序退出p所在作用域之前還是存在于棧中的,此段時(shí)間內(nèi)p指針稱為“懸垂指針”或“野指針”,p仍然指向被銷毀的對(duì)象的位置。在這段時(shí)間內(nèi),在win32平臺(tái)中,訪問p指針會(huì)出現(xiàn)3種情況:
①?第1種情況是該處位置所在的“內(nèi)存頁(yè)”沒有任何對(duì)象,堆管理器已經(jīng)將其進(jìn)一步返回給系統(tǒng),這種錯(cuò)誤會(huì)導(dǎo)致進(jìn)程崩潰;
?、?第2種情況是該處位置所在的“內(nèi)存頁(yè)”還有其他對(duì)象,且該處位置被回收后,尚未被分配出去,這時(shí)通過指針指針p訪問該處內(nèi)存,取得的值是無意義的,雖然不會(huì)立即引起進(jìn)程崩潰,但是針對(duì)該指針的后續(xù)操作的行為是不可預(yù)測(cè)的;
?、?第3種情況是該處位置所在的“內(nèi)存頁(yè)”還有其他對(duì)象,且該處位置被回收后,已被其他對(duì)象申請(qǐng),這時(shí)通過指針p訪問該處內(nèi)存,取得的值其實(shí)是程序其他處生成的對(duì)象。
創(chuàng)建一個(gè)對(duì)象分成兩個(gè)步驟:首先取得對(duì)象所需的內(nèi)存(無論是從線程棧還是從全局堆中),然后在該塊內(nèi)存上執(zhí)行構(gòu)造函數(shù)。在構(gòu)造函數(shù)構(gòu)建該對(duì)象時(shí),構(gòu)造函數(shù)也分成兩個(gè)步驟:第一步執(zhí)行初始化(通過初始化列表),第二步執(zhí)行構(gòu)造函數(shù)的函數(shù)體。(代碼舉例見后文)
對(duì)初始化操作有幾點(diǎn)需要注意:
① 構(gòu)造函數(shù)其實(shí)是一個(gè)遞歸操作,每層遞歸內(nèi)部的操作遵循嚴(yán)格的次序。遞歸模式為首先執(zhí)行父類的構(gòu)造函數(shù),父類構(gòu)造函數(shù)返回后構(gòu)造該類自己的成員變量。構(gòu)造該類的成員變量,一是嚴(yán)格按照成員變量在類中的聲明順序進(jìn)行,而與其在初始化列表中出現(xiàn)的順序完全無關(guān);二是當(dāng)有些成員變量或父類對(duì)象沒有在初始化列表中出現(xiàn)時(shí),它們?nèi)匀辉诔跏蓟僮鬟@一步驟中被初始化。
② 父類對(duì)象和一些成員變量沒有出現(xiàn)在初始化列表中時(shí),這些對(duì)象仍然被執(zhí)行構(gòu)造函數(shù),這時(shí)執(zhí)行的是“默認(rèn)構(gòu)造函數(shù)”。因此這些對(duì)象所屬的類必須提供可以調(diào)用的默認(rèn)構(gòu)造函數(shù),為此要求這些類要么自己“顯式”地提供默認(rèn)構(gòu)造函數(shù),要么不能阻止編譯器“隱式”地為其生成一個(gè)默認(rèn)構(gòu)造函數(shù),定義除默認(rèn)構(gòu)造函數(shù)之外的其他類型的構(gòu)造函數(shù)就會(huì)阻止編譯器生成默認(rèn)構(gòu)造函數(shù)。
?、?對(duì)兩類成員變量:“常量”(const)和“引用”(reference)。因?yàn)樗谐蓡T變量在執(zhí)行構(gòu)造函數(shù)函數(shù)體之前已經(jīng)被構(gòu)造,即已經(jīng)擁有初值。所以“常量”和“引用”類型必須在初始化列表中初始化,而不能將其初始化放在構(gòu)造函數(shù)函數(shù)體內(nèi)。
?、?在程序進(jìn)入構(gòu)造函數(shù)函數(shù)體之前,類的父類對(duì)象和所有子成員變量對(duì)象已經(jīng)被生成和構(gòu)造。如果在構(gòu)造函數(shù)體內(nèi)為其執(zhí)行賦值操作,顯示屬于浪費(fèi)。如果在構(gòu)造函數(shù)時(shí)已經(jīng)知道如何為類的子成員變量初始化,那么應(yīng)該將這些初始化信息通過構(gòu)造函數(shù)的初始化列表賦予子成員變量,而不是在構(gòu)造函數(shù)函數(shù)體中進(jìn)行這些初始化。因?yàn)檫M(jìn)入構(gòu)造函數(shù)函數(shù)體之前,這些子成員變量已經(jīng)初始化過一次了。
在C++程序中,創(chuàng)建/銷毀對(duì)象是影響性能的一個(gè)非常突出的操作。首先,如果是從全局堆中生成對(duì)象,則需要首先進(jìn)行動(dòng)態(tài)內(nèi)存分配操作。眾所周知,動(dòng)態(tài)分配/回收在C/C++程序中一直都是非常耗時(shí)的。因?yàn)闋可娴綄ふ移ヅ浯笮〉膬?nèi)存塊,找到后可能還需要截?cái)嗵幚?#xff0c;然后還需要修改維護(hù)全局堆內(nèi)存使用情況信息的鏈表等。因?yàn)橐庾R(shí)到頻繁的內(nèi)存操作會(huì)嚴(yán)重影響性能,所以已經(jīng)發(fā)展出很多技術(shù)用來緩解和降低這種影響,例如內(nèi)存池技術(shù)。
?????? 盡量少使用值傳遞,而使用常量引用傳遞。
3、繼承與虛擬函數(shù)
虛擬函數(shù)是C++語言引入的一個(gè)很重要的特性,它提供了“動(dòng)態(tài)綁定”機(jī)制,正是這一機(jī)制使得繼承的語義變得相對(duì)清晰。在繼承體系中如何聲明操作和變量:
?、?基類抽象了通用的數(shù)據(jù)及操作,就數(shù)據(jù)而言,如果該數(shù)據(jù)成員在個(gè)派生類中都需要用到,那么就需要將其聲明在基類中;就操作而言,如果該操作對(duì)各派生類都有意義,無論其語義是否會(huì)被修改或擴(kuò)展,都需要將其聲明在基類中。
② 有些操作,如果對(duì)于各個(gè)派生類而言,語義保持完成一致,而無需修改或擴(kuò)展,那么這些操作就應(yīng)該聲明為基類的非虛擬成員函數(shù)。各派生類默認(rèn)繼承了這些非虛擬函數(shù)的聲明/實(shí)現(xiàn),如同默認(rèn)繼承基類的數(shù)據(jù)成員一樣,而不必另外做任何聲明,這就是繼承帶來的代碼重用的優(yōu)點(diǎn)。
?、?另外還有一些操作,對(duì)于各派生類而言都有意義,但是其語義(實(shí)現(xiàn))并不相同。這時(shí),這些操作應(yīng)該聲明為基類的虛擬成員函數(shù)。各派生類雖然也默認(rèn)繼承了這些虛擬成員函數(shù)的聲明/實(shí)現(xiàn),但是語義上它們應(yīng)該對(duì)這些虛擬成員函數(shù)的實(shí)現(xiàn)進(jìn)行修改或擴(kuò)展。另外在實(shí)現(xiàn)這些修改或擴(kuò)展過程中,需要用到額外的該派生類獨(dú)有的數(shù)據(jù)時(shí),將這些數(shù)據(jù)聲明為此派生類自己的數(shù)據(jù)成員。
再考慮使用者對(duì)這個(gè)繼承體系的使用是如何實(shí)現(xiàn)多態(tài)、模塊化的。
當(dāng)更高層次的程序框架(繼承體系的使用者)使用此繼承體系時(shí),它處理的是一個(gè)抽象層次的對(duì)象集合(即基類)。雖然這個(gè)對(duì)象集合的成員實(shí)質(zhì)上可能是各種派生類對(duì)象,但在處理這個(gè)對(duì)象集合中的對(duì)象時(shí),它用的是抽象層次的操作。并不區(qū)分這些操作在派生類是否已經(jīng)做了修改,也是說使用者并不區(qū)分哪些是虛函數(shù)哪些是非虛函數(shù),也就是說使用者不理會(huì)它使用的是繼承體系中的哪個(gè)層次的類對(duì)象。這是因?yàn)?#xff0c;當(dāng)運(yùn)行時(shí)實(shí)際執(zhí)行到各操作時(shí),運(yùn)行時(shí)系統(tǒng)能夠識(shí)別哪些操作需要用到“動(dòng)態(tài)綁定”,從而找到應(yīng)該使用哪個(gè)類的函數(shù)。
因此,對(duì)繼承體系的使用者而言,此繼承體系是“透明的”。使用者不必關(guān)心繼承體系里邊的繼承關(guān)系是如何的錯(cuò)綜復(fù)雜,對(duì)它而言它所處理的是一致的對(duì)象,它只關(guān)心自己的業(yè)務(wù)邏輯。只要繼承體系提供的接口沒有變化,無論繼承體系內(nèi)部類的層次如何變更,使用者都不需要做任何改變,這使得程序可以模塊化,使用者就是一個(gè)程序模塊,這也意味著可擴(kuò)展性(繼承體系可以在需要的時(shí)候添加類)、可維護(hù)性(繼承體系可以修改內(nèi)部的實(shí)現(xiàn))、以及代碼的可讀性(結(jié)構(gòu)更清晰了)得到提高。(代碼舉例見后文)
虛函數(shù)帶來的開銷:
① ?空間:每個(gè)支持虛擬函數(shù)的類,都有一個(gè)虛擬函數(shù)表,這個(gè)虛擬函數(shù)表的大小跟該類擁有的虛擬函數(shù)的多少成正比,虛擬函數(shù)表屬于類所有,無論有多少個(gè)對(duì)象,都只有一個(gè)虛擬函數(shù)表。
?、?空間:通過支持虛擬函數(shù)的類生成的每一個(gè)對(duì)象都有一個(gè)指向該類虛擬函數(shù)表的指針。有多少個(gè)對(duì)象,就有多少個(gè)虛擬函數(shù)表指針。
?、?時(shí)間:支持虛擬函數(shù)的類的每一個(gè)對(duì)象,在構(gòu)造時(shí),都會(huì)初始化虛擬函數(shù)表指針,使其指向虛擬函數(shù)表。
?、?時(shí)間:當(dāng)通過指針或引用調(diào)用虛擬函數(shù)時(shí),跟普通函數(shù)調(diào)用相比,會(huì)多一個(gè)根據(jù)虛擬函數(shù)指針找到虛擬函數(shù)表的操作。
?、?可能無法使用內(nèi)聯(lián)函數(shù)。因?yàn)閮?nèi)聯(lián)函數(shù)是在“編譯期”,編譯期將調(diào)用內(nèi)聯(lián)函數(shù)的地方用內(nèi)聯(lián)函數(shù)體的代碼代替(內(nèi)聯(lián)展開),但是虛擬函數(shù)本質(zhì)上是“運(yùn)行期”行為。在“編譯期”,編譯器無法確定要?jiǎng)討B(tài)綁定的虛函數(shù)會(huì)綁定到那個(gè)函數(shù)上,所以無法內(nèi)聯(lián)展開。不過,如果在編譯時(shí)能夠確定調(diào)用哪個(gè)虛函數(shù),那還是可以內(nèi)聯(lián)的,只是,這樣它就失去了作為虛擬函數(shù)的功能。
據(jù)書上分析,采用虛擬函數(shù)跟不采用虛函數(shù)相比帶來的負(fù)面影響是:虛函數(shù)表的空間開銷和無法使用內(nèi)聯(lián)函數(shù)。虛函數(shù)表占的空間較小,可以忽略,所以主要缺點(diǎn)是無法使用內(nèi)聯(lián)函數(shù)。但是不采用虛函數(shù)就使得代碼可擴(kuò)展性和可維護(hù)性大大降低,而面向?qū)ο缶幊痰囊粋€(gè)重要目的就是增加程序的可擴(kuò)展性和可維護(hù)性,即當(dāng)程序的業(yè)務(wù)邏輯發(fā)生改變時(shí),對(duì)原程序的修改非常方便。
因此在性能和其他方面特性的選擇方面,需要開發(fā)人員根據(jù)實(shí)際情況進(jìn)行權(quán)衡和取舍。當(dāng)然在權(quán)衡之前,需要通過性能檢測(cè)確認(rèn)性能的瓶頸是由于虛擬函數(shù)沒有利用到內(nèi)聯(lián)函數(shù)的優(yōu)勢(shì)這一缺陷引起;否則可以不必考慮虛擬函數(shù)的影響。
4、臨時(shí)對(duì)象
這里所說的臨時(shí)對(duì)象是未出現(xiàn)在源碼中、不由程序員定義的,從棧中產(chǎn)生的未命名的對(duì)象,程序員可能沒有注意到它們的存在,它們是由編譯器根據(jù)需要產(chǎn)生、銷毀的。
書上的分析都是以編譯器未進(jìn)行優(yōu)化為基礎(chǔ)分析的,即分析的是VS編譯器的debug模式,VS編譯器的release模式做了很多的優(yōu)化,臨時(shí)對(duì)象已經(jīng)減少了很多。
里邊的一條建議確實(shí)可以減少臨時(shí)對(duì)象,release模式下也有用:對(duì)于非內(nèi)建類型的對(duì)象,盡量將對(duì)象的創(chuàng)建延遲到已經(jīng)確切知道其有效狀態(tài)時(shí)。
例如:(1)ClassA? a;
????????????? a= f();//f()內(nèi)部定義局部對(duì)象b,返回該局部變量。
?????? ?(2)ClassA a = f();
上邊的兩種代碼雖然功能一樣,但是在release模式下代碼(1)會(huì)多一次構(gòu)造函數(shù)操作和一次operator=操作。分析如下(代碼見后文)。
Release模式下:
(1)代碼分析:①構(gòu)造對(duì)象a;②構(gòu)造f()內(nèi)的“局部對(duì)象”b,release模式下b的地址空間是在外部分配的,所以再f()函數(shù)結(jié)束后,b對(duì)象仍然有效;③調(diào)用operator=操作符,把b對(duì)象賦值給a對(duì)象。
(2)代碼分析:①構(gòu)造f()內(nèi)的“局部對(duì)象”b,release模式下b的地址空間是在外部分配的,就是a對(duì)象的空間,所以返回后什么也不需要做。
operator+=跟operator+的比較。operator+=不需要產(chǎn)生臨時(shí)對(duì)象,operator+往往要產(chǎn)生臨時(shí)對(duì)象。所以盡量使用operator+=。
盡量使用++obj,而盡量不使用obj++。因?yàn)閛bj++會(huì)產(chǎn)生臨時(shí)對(duì)象用于返回++之前的值。
臨時(shí)對(duì)象的生命周期:從創(chuàng)建開始,到包含創(chuàng)建它的最長(zhǎng)語句執(zhí)行完畢。但有一個(gè)例外:當(dāng)用一個(gè)臨時(shí)對(duì)象來初始化一個(gè)常量引用時(shí),該臨時(shí)對(duì)象的生命會(huì)持續(xù)到與綁定到其上的常量引用銷毀時(shí)。
5、內(nèi)聯(lián)函數(shù)
?????? 內(nèi)聯(lián)函數(shù)與非內(nèi)聯(lián)函數(shù)的空間和時(shí)間比較。假設(shè)調(diào)用一個(gè)函數(shù)之前的準(zhǔn)備工作和之后的善后工作的指令所需空間大小為SS,執(zhí)行這些代碼所需時(shí)間為TS。
(1)空間。如果一個(gè)函數(shù)的函數(shù)體代碼大小為AS,在程序中被調(diào)用N次,不采用內(nèi)聯(lián)的情況下,空間開銷為:SS*N+AS。采用內(nèi)聯(lián):AS*N。因?yàn)镹一般很大,所以它們之間的比較就是SS跟AS的比較,得出的結(jié)論是:如果SS小于AS,不采用內(nèi)聯(lián),空間開銷更少。如果AS小于SS,則采用內(nèi)聯(lián),空間開銷更少。
(2)?時(shí)間。內(nèi)聯(lián)之后每次調(diào)用不再需要做函數(shù)調(diào)用的準(zhǔn)備和善后工作;內(nèi)聯(lián)之后編譯器獲得更多的代碼信息,可以進(jìn)行更好的代碼優(yōu)化;內(nèi)聯(lián)之后可以降低代碼“缺頁(yè)”的幾率。不過,如果內(nèi)聯(lián)的函數(shù)非常大的話,會(huì)使得存放代碼的頁(yè)面增多,“缺頁(yè)”也會(huì)相應(yīng)增加,速度反而下降,所以很大的函數(shù)不適合內(nèi)聯(lián)。
內(nèi)聯(lián)函數(shù)其他方面的負(fù)面影響:
(1)在一個(gè)大的工程中,某個(gè)內(nèi)聯(lián)函數(shù)被多次使用,如果修改了內(nèi)聯(lián)函數(shù),那么就要把用到它的所有編譯單元都重新編譯,可能會(huì)花費(fèi)大量的時(shí)間。
(2)如果某開發(fā)小組利用了第三方提供的程序庫(kù),使用了第三方的內(nèi)聯(lián)函數(shù),那么當(dāng)?shù)谌礁聝?nèi)聯(lián)函數(shù)的實(shí)現(xiàn)時(shí),開發(fā)小組要使用新的內(nèi)聯(lián)函數(shù)版本,就必須重新編譯,而如果此時(shí)開發(fā)小組的程序已經(jīng)發(fā)布了,那么要重新編譯代價(jià)是很高的。而如果不是采用內(nèi)聯(lián)函數(shù),那么就可以不必重新編譯即可利用新版本(可能是通過更新包含該函數(shù)的庫(kù)實(shí)現(xiàn)的)。
不可以使用內(nèi)聯(lián)函數(shù)的情況:
(1)遞歸調(diào)用。原因1可能不知道會(huì)遞歸多少次,所以無法內(nèi)聯(lián);原因2遞歸函數(shù)展開之后可能非常龐大,內(nèi)聯(lián)不合適。
(2)虛函數(shù)。原因在前邊已經(jīng)說過。但也存在例外,通過對(duì)象調(diào)用函數(shù),這種調(diào)用在編譯時(shí)可以確定調(diào)用哪個(gè)函數(shù),所以可以內(nèi)聯(lián)。不過,此時(shí)虛函數(shù)已經(jīng)失去了它本來的意義:“通過基類指針或引用調(diào)用,到真正運(yùn)行時(shí)才決定調(diào)用哪個(gè)版本”。
(3)程序入口main()函數(shù)肯定不會(huì)被內(nèi)聯(lián)。
最后說明:inline關(guān)鍵字僅僅是給編譯器一個(gè)建議,實(shí)際上編譯器會(huì)不會(huì)內(nèi)聯(lián)完全是它自己做決定。有的函數(shù)掉用即便不加上inline關(guān)鍵字,編譯器也會(huì)根據(jù)需要給內(nèi)聯(lián)了。
??
2010-8-22
cs_wuyg@126.com
?
?附測(cè)試代碼:
1、構(gòu)造函數(shù)是如何執(zhí)行的.cpp
//構(gòu)造函數(shù)是如何執(zhí)行的.cpp //2010.8.21 //coder:cs_wuyg@126.com //參考:《C++應(yīng)用程序性能優(yōu)化》2.1節(jié) /* 測(cè)試說明:通過輸出結(jié)果可以發(fā)現(xiàn),構(gòu)造函數(shù)執(zhí)行初始化的順序:生成父類實(shí)例-->生成子類實(shí)例;執(zhí)行初始化列表-->執(zhí)行構(gòu)造函數(shù)函數(shù)體;按照定義的順序初始化,跟初始化列表順序無關(guān)。 析構(gòu)函數(shù)順序:析構(gòu)子類實(shí)例-->析構(gòu)父類實(shí)例。 */ //VS2008 #include <iostream> using namespace std; // class BaseA {public:BaseA(int a = 10) : a(a){cout << "Base::BaseA(int a)" << "\t" << a << endl;}virtual ~BaseA(){cout << "BaseA::~BaseA()" << endl;}private:int a; };class BaseAA : public BaseA {public:BaseAA(int b = 10) : BaseA(b),b(b){cout << "BaseAA::baseAA(int b)" << "\t" << b << endl;}~BaseAA(){cout << "BaseAA::~BaseAA()" << endl;}private:int b; }; // class test {public:test(int t = 10) : t(t){cout << "test::test()" << "\t" << t << endl;}~test(){cout << "test::~test()" << endl;}private:int t; }; // class Derived : public BaseAA {public:Derived(int c = 10) : c(c), BaseAA(c), t2(c)//初始化列表中顯式的初始化了父類BaseAA,去掉BaseAA(c)后會(huì)發(fā)現(xiàn)輸出的值有變化{cout << "Derived::Derived(int c)" << "\t" << c << endl;}~Derived(){cout << "Derived::~Derived()" << endl;}private:int c;test t1;test t2;test t3; }; // int main() {BaseAA *p = new Derived(30);cout << "----------------------------------------" << endl;delete p;system("pause");return 0; } /* Base::BaseA(int a) 30BaseAA::baseAA(int b) 30 test::test() 10 test::test() 30 test::test() 10 Derived::Derived(int c) 30 ---------------------------------------- Derived::~Derived() test::~test() test::~test() test::~test() BaseAA::~BaseAA() BaseA::~BaseA() 請(qǐng)按任意鍵繼續(xù). . . */2、使用者跟繼承體系簡(jiǎn)單舉例.cpp
//使用者跟繼承體系簡(jiǎn)單舉例.cpp //2010.8.21 //coder:cs_wuyg@126.com //VS2005/2008 #include <iostream> using namespace std; / /*繼承體系*/ class Base {public:void common(){cout << "common work" << endl;}virtual void special() = 0; };class DerivedA : public Base {public:void special(){cout << "Derived A special work" << endl;} };class DerivedB : public Base {public:void special(){cout << "Derived B special work" << endl;} }; / /*使用者*/ class user {public:void usering(Base *p){p->common();//公有操作p->special();//專有操作} }; / int main() {DerivedA aobj;DerivedB bobj;user userobj;userobj.usering(&aobj);userobj.usering(&bobj);system("pause");return 0; } /* common work Derived A special work common work Derived B special work 請(qǐng)按任意鍵繼續(xù). . . */3、臨時(shí)對(duì)象release模式下臨時(shí)對(duì)象產(chǎn)生的測(cè)試.cpp
//臨時(shí)對(duì)象release模式下臨時(shí)對(duì)象產(chǎn)生的測(cè)試.cpp //2010.8.21 //coder:cs_wuyg@126.com //VS2005/2008編譯器, release模式 #include <iostream> using namespace std; // class Base {public:Base(){cout << "constructor" << endl;}Base(const Base& temp){cout << "copy constructor" << endl;}Base&operator=(const Base& temp){cout << "operator=" << endl;return *this;} }; // Base fun() {cout << "fun()" << endl;Base a;return a; } // int main() {//測(cè)試1cout << "----------測(cè)試1:" << endl;Base aobj = fun();//測(cè)試2cout << endl << "----------測(cè)試2:" << endl;Base aaobj;aaobj = fun();system("pause");return 0;} /* 測(cè)試結(jié)果1 ----------測(cè)試1: fun() constructor----------測(cè)試2: constructor fun() constructor operator= */ /* 通過測(cè)試結(jié)果可以發(fā)現(xiàn):“對(duì)于非內(nèi)建類型的對(duì)象,盡量將對(duì)象延遲到已經(jīng)確切知道其有效狀態(tài)時(shí)?!边@句話的正確性。 */4、臨時(shí)對(duì)象的生命周期.cpp
//臨時(shí)對(duì)象的生命周期.cpp //2010.8.21 //coder:cs_wuyg@126.com //參考《C++應(yīng)用程序性能優(yōu)化》2.3節(jié) //VS2005/2008編譯器 #include <iostream> #include <string> using namespace std;int main() {string stra = "frist";string strb = "second";const char* str;/*測(cè)試1*/cout << "----------測(cè)試1:" << endl;if (strlen(str = (stra + strb).c_str()) > 5){/*輸出失敗,因?yàn)榇藭r(shí)(stra + strb).c_str()這個(gè)臨時(shí)變量已經(jīng)失效了*/cout << str << endl;}/*測(cè)試2*/cout << "----------測(cè)試2:" << endl;const string& strc = stra + strb;cout << strc << endl;return 0; } /* 測(cè)試結(jié)果: ----------測(cè)試1:----------測(cè)試2: fristsecond */ /* 測(cè)試表明: 臨時(shí)對(duì)象的生命周期:從創(chuàng)建開始,到包含創(chuàng)建它的最長(zhǎng)語句執(zhí)行完畢。 但有一個(gè)例外:當(dāng)用一個(gè)臨時(shí)對(duì)象來初始化一個(gè)常量引用時(shí),該臨時(shí)對(duì)象的生命會(huì)持續(xù)到與綁定到其上的常量引用銷毀時(shí)。 */轉(zhuǎn)載于:https://www.cnblogs.com/cswuyg/archive/2010/08/22/1805840.html
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)總結(jié)
以上是生活随笔為你收集整理的《C++应用程序性能优化::第二章C++语言特性的性能分析》学习和理解的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Oops! the requested
- 下一篇: HDOJ 1247 HDU 1247 H