由创建一个不能被继承的类引发的对象模型的思考
昨天吃飯和寬妹討論起以前看過的如何創(chuàng)建一個(gè)不能被繼承的類,具體實(shí)現(xiàn)見
http://blog.csdn.net/kuaile123/article/details/21321471
同學(xué)提到了第一種方法,說new過之后還需要delete釋放內(nèi)存,這樣需要把析構(gòu)函數(shù)也設(shè)置為private。
然后就想到能不能創(chuàng)建一個(gè)變量,返回此變量,而不是new一個(gè),這樣可以將析構(gòu)函數(shù)設(shè)置為pubic不需要delete。寬妹說這種方法肯定行不通,因?yàn)檫@樣創(chuàng)建是在棧上創(chuàng)建的,當(dāng)返回時(shí)變量已經(jīng)無效了。事實(shí)證明是可行的。
class A{ private:int i;A( int j=0 ){ i=j; printf("construct: %d \n",i);} public:static A get_A0(){A a(2);return a;} }; A a0 = A::get_A0();//調(diào)用后討論說他說的是返回變量的地址是有錯(cuò)的,上述之所以成功是因?yàn)楹竺嬲{(diào)用后使用了一個(gè)復(fù)制構(gòu)造函數(shù),這樣會造成效率低。然后再次寫了返回變量地址的方法,如下所示:
//class A 定義里面 public:static A* get_A1(){A a(2); return &a; }A* a1 = A::get_A1();//調(diào)用顯示創(chuàng)建是沒用問題的,寬妹說調(diào)用此對象的一個(gè)成員函數(shù)肯定會出錯(cuò),由此,我們添加了一個(gè)成員函數(shù),如下所示:
//class A 定義里面 public : int f(){printf("%d \n",i);return i;} //調(diào)用 a1->f();輸出為:
construct: 2
4062396
而new的調(diào)用是這樣的:
static A* get_A2(){ return new A(3); } A* a2 = A::get_A2(); a2->f();輸出為:
construct: 3
3
顯示堆上創(chuàng)建的調(diào)用是沒有錯(cuò)的,棧上創(chuàng)建的調(diào)用結(jié)果是錯(cuò)誤的。因?yàn)闂I蟿?chuàng)建的是局部變量,用new是在堆上創(chuàng)建的,如果不手動釋放會一直存在,而在棧上創(chuàng)建的出了作用域會失效,所以在堆上創(chuàng)建調(diào)用函數(shù)時(shí)沒有問題的。
但是依然可以打印說明仍然可以進(jìn)入函數(shù),但是只是打印成員數(shù)據(jù)時(shí)出錯(cuò),此時(shí)我們討論了C++對象模型,寬妹認(rèn)為成員函數(shù)是不放置在類對象里的,而是經(jīng)過混淆放置于一個(gè)全局的位置,所以雖然類對象是錯(cuò)誤的,但是依然可以調(diào)用成員函數(shù)。為此又寫了一個(gè)調(diào)用方法來證實(shí),將隨便一個(gè)變量的地址賦值給類對象,照樣能打印,數(shù)據(jù)還是此變量的值,如下所示:
int i =100;A* a = reinterpret_cast<A*>(&i);a->f();輸出為:
100
由此可見沒有調(diào)用構(gòu)造函數(shù)直接訪問了i的地址。我們翻了《C++對象模型》這本書,討論了出現(xiàn)這種結(jié)果的原因,C++對象模型中,只有非靜態(tài)數(shù)據(jù)成員被放置在每一個(gè)類對象中,而其他的如靜態(tài)數(shù)據(jù)成員、靜態(tài)和非靜態(tài)函數(shù)成員都是放置于類對象外,可以理解為一個(gè)類的空間中,每個(gè)對象只是共享同一份。這樣也就解釋了為什么類對象錯(cuò)誤但是依然能夠調(diào)用類成員的原因。當(dāng)創(chuàng)建一個(gè)類變量返回該變量的地址時(shí),復(fù)制該指針,而當(dāng)退出此函數(shù)時(shí)隨即此變量已經(jīng)失效,a1指針實(shí)際上指向的是一塊無效的棧空間,當(dāng)調(diào)用函數(shù)f()時(shí),只是從類中調(diào)用,不經(jīng)過類對象a1,f()中需要對象的數(shù)據(jù)成員i,就到認(rèn)為正確的地址空間去找,a1+數(shù)據(jù)成員在a1中的偏移,而此時(shí)此段可能已經(jīng)被改寫,此時(shí)不會返回正確的結(jié)果。同理,我們隨便將一個(gè)變量的地址強(qiáng)制轉(zhuǎn)換為類對象的指針,此時(shí)也是上述的流程,上述巧合是因?yàn)榈谝粋€(gè)聲明的成員變量地址和類對象地址是一致的,而且對象中的i和所聲明變量都是int型,讀取時(shí)都會讀取i的地址,當(dāng)我們將變量設(shè)置為float等,此時(shí)就會有截?cái)嗟?#xff0c;打印出的也不是變量的值。
有些問題在書上看到過,但是距離用理論來分析還是有一定距離,幸而周圍有幾個(gè)小伙伴,尤其是寬妹,讓我對一些東西的理解更深一些。三個(gè)臭皮匠頂個(gè)諸葛亮,O(∩_∩)O哈哈~
總結(jié)
以上是生活随笔為你收集整理的由创建一个不能被继承的类引发的对象模型的思考的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。