复制构造函数的用法及出现迷途指针问题
?復制構造函數利用下面這行語句來復制一個對象:
? A (A &a)
?從上面這句話可以看出,所有的復制構造函數均只有一個參數,及對同一個類的對象的引用
?
比如說我們有一個類A,定義如下:
?
?| 1 2 3 4 5 6 7 8 9 10 | class A { public: A(int i,int j){n=i;m=j;} //構造函數帶兩個參數,并將參數的值分別賦給兩個私有成員 A(A &t); //我們自己定義一個默認構造函數 void print(){cout<<n<<m;}//輸出兩個成員的值 private: int n;? //數據成員n int m; //數據成員m }; |
?
????? 在上面這個類的定義中我們定義了一個默認的構造函數(雖然默認構造函數一般是通過編譯器自動定義的,但是這里我們模擬一下它的工作過程)。默認構造函數的工作方法應該如下面所示:
?
?| 1 2 3 4 | A(A &t) { ????????n=t.n;m=t.m; } |
?
????? ?在這里我們模擬了一個默認復制構造函數是如何運行的。它通過別名t訪問一個對象,并將該對象的成員賦給新對象的成員。這樣就完成了復制工作。這樣一來,我們如果再程序中定義了:
?
?| A a(2,4); |
?
?????? 這個對象。這就表示,利用構造函數,對象a的數據成員a.n=2;a.m=4,如果我們利用下面這句話調用一個默認的復制構造函數:
?
?| A b(a) |
?
?????? 那么它就會調用復制構造函數,即上面我們模擬的復制構造函數中所定義的“n=t.n;m=t.m”。這時對象b的數據成員b.n=2;b.m=4,這與對象a中的數據成員的值應該是一模一樣的。
?
迷途指針產生的原因:“淺層復制構造函數”
????? 一般地,編譯器提供的默認復制構造函數的功能只是把傳遞進來的對象的每一個成員變量的值復制給了新對象的成員變量。但是,如果這個老對象是一個指針,那么新對象也是一個指針,且它指向的內存地址和老對象是一樣的!這樣就會產生2個顯而易見的問題:
輸出結果:
?
????? 上面紅色框就代表了用a.set(32)來改變a中指針x所指向的內存空間的值,可以看出b的指針x所指向的內存空間的值也跟著變成了32。綠色框代表了用b.set(99)來改變b中指針x所指向的內存空間的值,可以看出a的指針x所指向的內存空間的值也跟著變成了99。而在程序崩潰對話框(白底黑字那個)中指明了程序的第52行引發了一個錯誤,大意就是那個內存區域是非法的(如上圖中藍色框所示)。出現這個錯誤的原因其實就是由迷途指針造成的:當main函數結束(右大括號)并析構對象b的時候,由于指針成員x所指向的內存區域已經在第52行被釋放了,這樣一來,對象b中的指針x就變成了迷途指針了,我們再次釋放這塊內存空間的時候肯定就會導致程序的崩潰。
上篇文章末尾談到了指針懸掛的問題,這主要是由于淺層復制構造函數的原因。為了解決這個指針懸掛的問題,這時候我們就需要引進一個新的概念:深層復制構造函數。
????? 下面,我們來介紹一下淺層復制構造函數與深層復制構造函數之間的區別與聯系......
- 淺層復制構造函數:淺層構造賦值函數主要是將傳遞進來的對象的成員變量的所有值賦值給新對象的成員變量。
????? 上面這個語句就會產生一個問題,即對象b的指針成員變量b.x和對象a的指針成員變量a.x所保存的值是同一塊內存空間的地址。如果我們析構了對象a,那么編譯器會自動釋放該內存地址,而b并不知道,這樣就產生了指針懸掛的問題。這就是由于淺層復制構造函數的運作機理產生的,它只是將舊對象的數據復制給新對象的數據,而如果是指針對象,它復制的就是指針所保存的地址。上面這句話是不是有點繞啊,我們用下圖來解釋一下: ????? 從上面這個圖就可以非常清楚地看到,當我 們析構掉對象a的時候,編譯器會自動釋放堆中所創建的內存空間。而對于對象b而言,它壓根就不知道有編譯器釋放堆中內存這么一回事,所以自然b.x就變成迷途指針了。x=a.x;//把對象a中的指針成員變量x的值復制給了對象b中的指針成員變量x - 深層復制構造函數:淺層構造賦值函數主要功能雖然也是是將傳遞進來的對象的成員變量的所有值賦值給新對象的成員變量,但是有一點不同之處在于,先看程序: ?
由上面的程序可以看出,其實深層復制構造函數中我們只添加了為成員指針指向的數據成員分配內存,同時在賦值的時候,我們是把舊對象中指針成員所指向的值復制給了新對象的指針成員,而不是地址,這樣就可以避免指針懸掛的問題了。你可能會問,上面這么長一串話是啥意思哦?不解釋,我們直接上圖!1 2 x=new int;//創建一塊新空間 *x=*(a.x);//把對象a中指針成員x所指向的值賦值給了利用深層賦值構造函數所創建的對象b的指針成員x
????? 從上面這個圖,我們就顯而易見地看出深層復制構造函數的好處了!注意看如果我們析構了對象a,那么編譯器只是會回收內存地址為A處得內存,而不會管內存地址為D處的值。這樣就避免了利用淺層復制函數所產生的對象b的指針懸掛問題了。
轉載于:https://www.cnblogs.com/wft1990/p/6617416.html
總結
以上是生活随笔為你收集整理的复制构造函数的用法及出现迷途指针问题的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 梦到大门牙掉了是什么意思
- 下一篇: 转HTML、CSS、font-famil