通常我們都將構造函數的聲明置於public區段,假如我們將其放入private區段中會發生什么樣的后果?沒錯,我也知道這將會使構造函數成為私有的,這意味着什么?
我們知道,當我們在程序中聲明一個對象時,編譯器為調用構造函數(如果有的話),而這個調用將通常是外部的,也就是說它不屬於class對象本身的調用,假如構造函數是私有的,由於在class外部不允許訪問私有成員,所以這將導致編譯出錯。
你於是說:“哈哈。”我們制造了一個似乎無法產生對象的class.哦,當然,對於class本身,我們還可以利用它的static公有成員,因為它們獨立於class對象之外,我們不必產生對象也可以使用它們。嗯,看來我們還是為帶有私有構造函數的類找到了一個存在的理由。不過我們不應當滿足於此,因為看上去應當還有發掘的余地。
首先我們來認真看一下是不是真的無法創建出一個具有私有構造函數的類對象。“呃,可能未必。”你現在也許會這樣說。這很好,讓我們再來看看為什么,沒錯,因為構造函數被class私有化了,所以我們要創建出對象,就必須能夠訪問到class的私有域;但這一點“我們”是做不到的,那么,誰能做得到呢?class的成員可以做得到;但在我們建構出其對象之前,怎么能利用它的成員呢?噢,剛才我們剛剛提到了static公有成員,它是獨立於class對象而存在的,當然,它也是公有的,“我們”可以訪問得到。假如在某個static函數中創建了該class的對象,並以引用或者指針的形式將其返回(不可以以值的形式返回,想想為什么),我們就獲得了這個對象的使用權。下面是例子:
| class WonderfulClass { public: static WonderfulClass* makeAnObject() { // 創建一個WonderfulClass對象並返回其指針 return (new WonderfulClass); } private: WonderfulClass() { } }; int main() { WonderfulClass *p = WonderfulClass::makeAnObject(); ... // 使用*p delete p; // Not neccesary here, but it's a good habit. return 0; } |
嗯,這個例子使用了私有構造函數,但它運行得很好:makeAnObject()作為WonderfulClass的靜態成員函數,盡心盡責地為我們創建對象:由於要跨函數傳遞並且不能使用值傳遞方式,所以我們選擇在堆上創建對象,這樣即使makeAnObject()退出,對象也不會隨之蒸發掉,當然,使用完之后你可不要忘了手工將它清除。
回到前面的思路:除了公有的static成員可以幫助我們訪問私有域外,還有沒有其它可以利用的“東西”?
噢,你一定想到了使用友元,完全正確。可以使用該類的友元函數或者友元類創建其對象,這里就不舉例了。
我們知道沒有人會無聊到無緣無故把一個class設為私有,然后再寫一個和上面一模一樣的makeAnObject()來讓它的用戶體驗一下奇特的感覺。我們也不太相信這只是由於C++的設計原因而導致的一個“順便的”“特殊的”“無用的”邊角功能。它應當是有實際用途的。提醒一下,到了JAVA中你會更容易明白很多靜態方法創建對象的原理!!!
嗯,例如,我們想實現這樣一個class:它至多只能存在一個,或者指定數量個的對象(還記得標准輸入輸出流庫中那個獨一無二的cout嗎?),我們可以在class的私有域中添加一個static類型的計數器,它的初值置為0,然后再對makeAnObject()做點手腳:每次調用它時先檢查計數器的值是否已經達到對象個數的上限值,如果是則產生錯誤,否則才new出新的對象,同時將計數器的值增1.最后,為了避免值復制時產生新的對象副本,除了將構造函數置為私有外,復制構造函數也要特別聲明並置為私有。
后記:
上面的仁兄評論堪稱經典,總結一下就是為了避免創建對象時調用類的構造函數,而是想將生成對象的方式以其它的方式實現,則可將所有的構造函數聲明為非public的,這樣並不意味着該類無意義,不能實例化生成對象。
生成對象可以兩種方式:
1. 通過同時為該類聲明Public的static成員函數,在該static成員函數中調用該類私有的構造函數,生成實例,static成員函數是屬於任何一個對象,而是屬於類的,故可以在沒有該類的對象的情況下,通過<類名>::<static 公有成員函數名>(參數)的方式來實現。
2. 調用該類的私有構造函數並生成類對象實例還可以通過該類的友元函數,或該類的友元類的成員函數調用此私有構造函數來實現。
