VTK修炼之道80:VTK开发基础_智能指针与引用计数
1.引用計(jì)數(shù)
VTK經(jīng)過多年的開發(fā)與維護(hù),已經(jīng)形成了一套穩(wěn)定的框架和開發(fā)規(guī)則。因此,了解這些規(guī)則和框架是定制VTK類的基礎(chǔ),這其中用到了大量面向?qū)ο蟮脑O(shè)計(jì)模式,例如對象工程模式、觀察者/命令模式;還有就是當(dāng)下非常流行的引用計(jì)數(shù)與智能指針等高級內(nèi)存管理等。
內(nèi)存管理在大型的工程中是非常重要的內(nèi)容,如果不能有效地管理內(nèi)存,將嚴(yán)重影響到應(yīng)用程序的執(zhí)行效率,甚至可能造成程序崩潰。之前學(xué)習(xí)C++基礎(chǔ)時(shí),教材中都會反復(fù)的強(qiáng)調(diào),使用new操作符申請的空間,一定要使用delete來釋放。C++中并沒有提供高級的內(nèi)存管理與垃圾回收機(jī)制,通常進(jìn)行手動管理。這對于簡單的程序而言可以輕松完成,但是對于大型程序就會疲于應(yīng)付。最有代表性例子就是,當(dāng)一個(gè)內(nèi)存塊(可以看做一個(gè)指針)被多個(gè)對象引用時(shí),刪除任意一個(gè)對象,都可能影響其他對象,引用計(jì)數(shù)和智能指針剛好用來解決這個(gè)問題。
1.1 引用計(jì)數(shù)
簡單來說,引用計(jì)數(shù)就是每個(gè)對象中維護(hù)一個(gè)引用計(jì)數(shù)的變量,表示當(dāng)前對象被多少對象引用。
當(dāng)一個(gè)對象被另一個(gè)對象引用時(shí),該對象的引用計(jì)數(shù)就會加1;當(dāng)一個(gè)對象取消對該對象的引用時(shí),該對象的引用計(jì)數(shù)減1.當(dāng)引用計(jì)數(shù)為0時(shí),程序就會撤銷該對象。
VTK中實(shí)現(xiàn)引用計(jì)數(shù)的類為vtkObjectBase。VTK是一個(gè)C++類庫,在VTK中,C++的繼承與多態(tài)得到了完美的體現(xiàn)。經(jīng)過十幾年的發(fā)展,所有的VTK類集合可以看做一個(gè)樹狀結(jié)構(gòu),vtkObjectBase則是他們共同的祖先。
這也說明了絕大部分VTK類都支持引用計(jì)數(shù)。vtkObjectBase中定義了一個(gè)ReferenceCount變量,改變量記錄了引用計(jì)數(shù)。當(dāng)一個(gè)vtkObjectBase及其子類對象創(chuàng)建時(shí),ReferenceCount就會被初始化為1。
vtkObjectBase::vtkObjectBase {this->ReferenceCount = 1; }在該類中,vtkObjectBase的構(gòu)造函數(shù)、析構(gòu)函數(shù)、拷貝構(gòu)造函數(shù)以及“=”操作符都被聲明為“protected”類型,因此不能顯示地構(gòu)造和銷毀vtkObjectBase及其子類對象。vtkObjectBase定義了一個(gè)靜態(tài)函數(shù)New(),用于生成vtkObjectBase對象。New()函數(shù)中調(diào)用了構(gòu)造函數(shù)來生成一個(gè)對象,并在構(gòu)造函數(shù)中初始化引用計(jì)數(shù)為1.1.2?Register()函數(shù)以及Unregister()函數(shù)實(shí)現(xiàn)計(jì)數(shù)
生成一個(gè)vtkObjectBase及其子類對象后,該對象并不會孤立地存在,多數(shù)情況下又可能被其他對象引用。這是需要調(diào)用Regester()函數(shù)實(shí)現(xiàn)引用計(jì)數(shù)加1;Register()函數(shù)中有一個(gè)vtkObjectBase*類型的形參,代表引用當(dāng)前對象的其他對象的類型(可以設(shè)置為NULL)。因此,引用計(jì)數(shù)關(guān)心的是被引用的數(shù)量,而不關(guān)心引用者是誰。而Unregister()函數(shù)實(shí)現(xiàn)引用計(jì)數(shù)減1,并檢查引用計(jì)數(shù)的數(shù)量。當(dāng)引用計(jì)數(shù)為零時(shí),自動銷毀該對象。
1.3?Delete()函數(shù)刪除對象釋放內(nèi)存
對于New()的對象,一定要通過Delete()對象來刪除。Delete()函數(shù)并非直接刪除對象,而式調(diào)用Unregister()對象將引用計(jì)數(shù)減1,如果引用計(jì)數(shù)為0,則調(diào)用析構(gòu)函數(shù)來刪除對象。
一個(gè)簡單的實(shí)例如下:
vtkCamera* camera = vtkCamera::New(); //引用計(jì)數(shù)為1 renderer->SetActiveCamera(camera); //引用計(jì)數(shù)為2 renderer->Delete(); //引用計(jì)數(shù)是1 camera->Delete(); //camera被刪除首先調(diào)用vtkCamera::New()函數(shù)實(shí)例化一個(gè)vtkCamera對象camera,此時(shí)camera的引用計(jì)數(shù)初始化為1.然后將camera通過SetActiveCamera()函數(shù)傳遞至一個(gè)vtkRenderer對象renderer中。
vtkRenderer::SetActiveCamera()函數(shù)的代碼如下:
void vtkRenderer::SetActiveCamera(vtkCamera* cam) {if(this->ActiveCamera == cam){return;}if(this->ActiveCamera){this->ActiveCamera->UnRegister(this);this->ActiveCamera = NULL;}if( cam ){cam->Register( this );}this-<ActiveCamera == cam;this->Modified();this->InvokeEvent(vtkCommand::ActiveCameraEvent, cam); }解釋:
vtkRenderer中定義了一個(gè)vtkCamera對象ActiveCamera。SetActiveCamera()函數(shù)用于設(shè)置該對象的值。在調(diào)用SetActiveCamera()函數(shù)時(shí),如果當(dāng)前已經(jīng)設(shè)置了ActiveCamera,則先UnRegister()該對象,并將其指向NULL。然后,調(diào)用Register()函數(shù)增加一個(gè)引用,說明camera在renderer中被應(yīng)用,并將ActiveCamera指向camera。此時(shí),camera的引用計(jì)數(shù)數(shù)目為2(如果這里又有一個(gè)新的vtkCamera對象通過SetActiveCamera設(shè)置,同樣先將此前設(shè)置的camera對象引用計(jì)數(shù)減1,再賦值)。而當(dāng)執(zhí)行renderer->Delete()函數(shù)時(shí),由于renderer的引用計(jì)數(shù)為1,所以renderer會被銷毀,而此時(shí)camera又變?yōu)榱?.當(dāng)執(zhí)行camera->Delete()后,其引用計(jì)數(shù)減一,此時(shí)camera計(jì)數(shù)為零,刪除camera對象。
1.4 引用計(jì)數(shù)的先天缺陷
其實(shí),引用計(jì)數(shù)并不是十分的完美。本身就有先天的缺陷——對循環(huán)引用無能為力。即無法處理對象之間相互引用形成一個(gè)環(huán)路的情況,例如,VTK中vtkAlgrthm和vtkExecute對象之間。2.智能指針
智能指針可以完全避免內(nèi)存泄漏問題。
2.1 智能指針
VTK中智能指針類為vtkSmartPointer。VTKSmartPointer是一個(gè)模板類,繼承自VTKSmartPointerBase類。VTKSmartPointerBase中定義了一個(gè)vtkObjectBase類型的指針對象Object,用于存儲智能指針中實(shí)際生成的對象。智能指針定義為:
vtkSmartPoint<vtkCamera> camera = vtkSmartPointer<vtkCamera>::New(); //引用計(jì)數(shù)為1VTKSmartPointer中定義了靜態(tài)函數(shù)New()來生成一個(gè)智能指針對象。
該函數(shù)的核心在于:會根據(jù)模板參數(shù)類型來生成一個(gè)對象,并將其保存在基類VTKSmartPointerBase的成員變量Object中。
- VTKSmartPointer中重載了“->”操作符
- VTKSmartPointer重載了“=”操作符
例如:
vktSmartPointer<vtkCamera> camera1 =vtkSmartPointer<vtkCamera>::New(); vtkSmartPointer<vtkCamera> camera2 = camera1;需要注意的是,此時(shí)camera1和camera2的引用計(jì)數(shù)都等于2。
過程為:首先camera1的vtkCamera對象Object調(diào)用Register()函數(shù),自動將引用計(jì)數(shù)加1,談后將camera2的Object指向camera1的Object對象。
2.2 智能指針釋放內(nèi)存
當(dāng)一個(gè)智能指針對象的生命周期結(jié)束時(shí),會自動調(diào)用其析構(gòu)函數(shù)釋放內(nèi)存。在析構(gòu)函數(shù)中會調(diào)用其內(nèi)部對象Object的UnRegister()函數(shù)修改引用計(jì)數(shù)。如果此時(shí)的引用計(jì)數(shù)為0,Object對象會自動釋放內(nèi)存。
3. vtkObjectBase中的幾個(gè)重要函數(shù)
3.1 GetClassName()
該函數(shù)用于返回當(dāng)前類的名字。其通過調(diào)用類內(nèi)保護(hù)類型的虛函數(shù)GetClassNameInternal()來實(shí)現(xiàn)。vtkObjectBase時(shí)VTK中絕大對數(shù)類的基類,因此這些類都可以訪問GetClassName()函數(shù)來獲取類名。我們必須在這子類中覆蓋GetClassNameInternal()函數(shù),這樣才會有“多態(tài)性”效應(yīng)。
3.2 IsTypeOf/IsA()
IsTypeOf()是一個(gè)靜態(tài)函數(shù),其參數(shù)為一個(gè)char類型字符串,通常為一個(gè)類的名字,用于判斷一個(gè)類名是否為vtkObjectBase。
虛函數(shù)IsA()則調(diào)用IsTypeOf()來判斷一個(gè)對象的類型。vtkObjectBase的類中都覆蓋了IsA(),以便判斷實(shí)際的類型。
3.3 Print()/PrintSelf()/PrintHeader()/PrintTrailer()
Print()用于輸出類的成員變量和狀態(tài),其內(nèi)部調(diào)用PrintSelf()/PrintHeader()/PrintTrailer()三個(gè)虛函數(shù)。
4. 常用vtkObjectBase子類及其繼承關(guān)系
- vtkCommand主要涉及觀察者/命令模式的實(shí)現(xiàn)。
- vtkInformationKey與vtkInformation搭配使用,用于實(shí)現(xiàn)VTK的執(zhí)行管線。
- vtkObject是一個(gè)非常非常重要的基類!!其子類包括vtkAlgrithm和vtkExecutive兩個(gè)實(shí)現(xiàn)Filter類最重要的類;而vtkDataObject是VTK中所有數(shù)據(jù)結(jié)構(gòu)類如:vtkPolyData、vtkImageData等的基類。
5.參看資料
1.《C++ primer》
2.《The VTK User’s Guide – 11thEdition》
3. ?張曉東, 羅火靈. VTK圖形圖像開發(fā)進(jìn)階[M]. 機(jī)械工業(yè)出版社, 2015.
總結(jié)
以上是生活随笔為你收集整理的VTK修炼之道80:VTK开发基础_智能指针与引用计数的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: VTK修炼之道79:交互与拾取_单位拾取
- 下一篇: 企业巧妙运用飞秋提高工作效率