国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > C++ > 正文

C++教程:C++拷貝構(gòu)造函數(shù)

2020-05-23 14:25:55
字體:
供稿:網(wǎng)友
我們在程序中常常需要把一些數(shù)據(jù)復制一份出來備作它用。對于只有基本類型變量的程序來說,這是輕而易舉就能做到的——新建一個臨時變量,用一句賦值語句就能完成。但如果它是一個有著許許多多成員數(shù)據(jù)的對象,這就會非常麻煩。最要命的是,那些成員數(shù)據(jù)還是私有的,根本無法直接訪問或修改。那么這時候,我們怎么“克隆”出一個和原來的對象相同的新對象呢?

拷貝構(gòu)造函數(shù)

我們知道,構(gòu)造函數(shù)是可以帶上參數(shù)的,這些參數(shù)可以是整型、字符型等等,那么它可不可以是一個對象類型呢?我們把一個原來的對象丟給構(gòu)造函數(shù),然后讓它給我們造一個相同的對象,是否可以呢?下面我們就來試試看:(程序15.4.1)
//node.h
#include <iostream>
using namespace std;
class Node//定義一個鏈表結(jié)點類
{
   public:
   Node();//構(gòu)造函數(shù)的聲明
   Node(int i,char c='0');//構(gòu)造函數(shù)重載1
   Node(int i,char c,Node *p,Node *n);//構(gòu)造函數(shù)重載2
   Node(Node &n);//結(jié)點拷貝構(gòu)造函數(shù),&表示引用
   int readi() const;//讀取idata
   char readc() const;//讀取cdata
   Node * readp() const;//讀取上一個結(jié)點的位置
   Node * readn() const;//讀取下一個結(jié)點的位置
   bool set(int i);//重載,通過該函數(shù)修改idata
  bool set(char c);//重載,通過該函數(shù)修改cdata
   bool setp(Node *p);//通過該函數(shù)設(shè)置前驅(qū)結(jié)點
   bool setn(Node *n);//通過該函數(shù)設(shè)置后繼結(jié)點
   private:
   int idata;//存儲數(shù)據(jù)保密
   char cdata;//存儲數(shù)據(jù)保密
   Node *prior;//前驅(qū)結(jié)點的存儲位置保密
   Node *next;//后繼結(jié)點的存儲位置保密
};
//未定義的函數(shù)與程序15.2.2相同
Node::Node(Node &n)
{
   idata=n.idata;//可以讀出同類對象的私有成員數(shù)據(jù)
   cdata=n.cdata;
   prior=n.prior;
   next=n.next; 
}
//linklist.h
#include "node.h"//需要使用鏈表結(jié)點類
#include <iostream>
using namespace std;
class Linklist
{
   public:
   Linklist(int i,char c);//鏈表類構(gòu)造函數(shù)
   Linklist(Linklist &l);//鏈表拷貝構(gòu)造函數(shù),&表示引用
   bool Locate(int i);//根據(jù)整數(shù)查找結(jié)點
   bool Locate(char c);//根據(jù)字符查找結(jié)點
   bool Insert(int i=0,char c='0');//在當前結(jié)點之后插入結(jié)點
   bool Delete();//刪除當前結(jié)點
   void Show();//顯示鏈表所有數(shù)據(jù)
   void Destroy();//清除整個鏈表
   private:
   Node head;//頭結(jié)點
   Node * pcurrent;//當前結(jié)點指針
};
//未定義的函數(shù)與程序15.3相同
Linklist::Linklist(Linklist &l):head(l.head)//調(diào)用結(jié)點的拷貝構(gòu)造函數(shù)來初始化head
{
   cout<<"Linklist cloner running..." <<endl;
   pcurrent=l.pcurrent;//指針數(shù)據(jù)可以直接賦值
}
//main.cpp
#include "Linklist.h"
#include <iostream>
using namespace std;
int main()
{
   int tempi;
   char tempc;
   cout <<"請輸入一個整數(shù)和一個字符:" <<endl;
   cin >>tempi >>tempc;
   Linklist a(tempi,tempc);
   a.Locate(tempi);
   a.Insert(1,'C');
   a.Insert(2,'B');
   a.Insert(3,'F');
   cout <<"After Insert" <<endl;
   a.Show();
   a.Locate('B');
   a.Delete();
   cout <<"After Delete" <<endl;
   a.Show();
   Linklist b(a);//創(chuàng)建一個鏈表b,并且將鏈表a復制到鏈表b
   cout <<"This is Linklist b" <<endl;
   b.Show();//顯示b鏈表中的內(nèi)容
   a.Destroy();
   cout <<"After Destroy" <<endl;
   a.Show();
   return 0;
}

運行結(jié)果:
請輸入一個整數(shù)和一個字符:
4 G
Node constructor is running...
Linklist constructor is running...
Node constructor is running...
Node constructor is running...
Node constructor is running...
After Insert
4 G
3 F
2 B
1 C
After Delete
4 G
3 F
1 C
Linklist cloner running...
This is Linklist b
4 G
3 F
1 C
After Destroy
4 G
根據(jù)程序運行的結(jié)果,我們發(fā)現(xiàn)輸出鏈表b的內(nèi)容的確和鏈表a一樣了,并且我們可以得到三個結(jié)論:
(1) 拷貝構(gòu)造函數(shù)可以讀出相同類對象的私有成員數(shù)據(jù);
(2) 拷貝構(gòu)造函數(shù)的實質(zhì)是把參數(shù)的成員數(shù)據(jù)一一復制到新的對象中;
(3) 拷貝構(gòu)造函數(shù)也是構(gòu)造函數(shù)的一種重載。

默認拷貝構(gòu)造函數(shù)

我們已經(jīng)知道構(gòu)造函數(shù)有默認構(gòu)造函數(shù),其實拷貝構(gòu)造函數(shù)也有默認的拷貝構(gòu)造函數(shù)。所謂默認拷貝構(gòu)造函數(shù)是指,用戶沒有自己定義拷貝構(gòu)造函數(shù)時,系統(tǒng)自動給出的一個拷貝構(gòu)造函數(shù)。默認拷貝構(gòu)造函數(shù)的功能是將對象的成員數(shù)據(jù)一一賦值給新創(chuàng)建的對象的成員數(shù)據(jù),如果某些成員數(shù)據(jù)本身就是對象,則自動調(diào)用它們的拷貝構(gòu)造函數(shù)或默認拷貝構(gòu)造函數(shù)。

拷貝構(gòu)造函數(shù)存在的意義

既然上面說到,默認拷貝構(gòu)造函數(shù)已經(jīng)能夠滿足我們大多數(shù)的需要,那么自定義的拷貝構(gòu)造函數(shù)是否就可以不用存在了呢?我們修改一下程序15.4.1的main.cpp文件,看看拷貝構(gòu)造函數(shù)到底有什么意義:
#include "Linklist.h"
#include <iostream>
using namespace std;
int main()
{
   int tempi;
   char tempc;
   cout <<"請輸入一個整數(shù)和一個字符:" <<endl;
   cin >>tempi >>tempc;
   Linklist a(tempi,tempc);
   a.Locate(tempi);
   a.Insert(1,'C');
   a.Insert(2,'B');
   a.Insert(3,'F');
   cout <<"After Insert" <<endl;
   a.Show();
   a.Locate('B');
   a.Delete();
   cout <<"After Delete" <<endl;
   a.Show();
   Linklist b(a);
   cout <<"This is Linklist B" <<endl;
   b.Show();
   a.Destroy();
   cout <<"After Destroy" <<endl;
   a.Show();
   cout <<"This is Linklist b" <<endl;
   b.Show();//關(guān)鍵是在這里加了一條語句,讓它顯示b鏈表中的內(nèi)容
   return 0;
}
運行結(jié)果:
C++教程:C++拷貝構(gòu)造函數(shù)
為什么顯示鏈表b的內(nèi)容,卻導致了嚴重的錯誤呢?

這時我們就要來研究一下這個鏈表的結(jié)構(gòu)了。在這個鏈表中,成員數(shù)據(jù)只有頭結(jié)點head和當前指針pcurrent,所有的結(jié)點都是通過new語句動態(tài)生成的。而程序15.4.1中的拷貝構(gòu)造函數(shù)僅僅是簡單地將頭結(jié)點head和當前指針pcurrent復制了出來,所以一旦運行a.Destroy()之后,鏈表a頭結(jié)點之后的結(jié)點已經(jīng)全部都刪除了,而鏈表b的頭結(jié)點還傻傻地指向原來a鏈表的結(jié)點。如果這時再訪問鏈表b,肯定就要出問題了。如下所示:
C++教程:C++拷貝構(gòu)造函數(shù)
程序15.4.1中的拷貝構(gòu)造函數(shù)僅僅是把成員數(shù)據(jù)拷貝了過來,卻沒有把動態(tài)申請的資源拷貝過來,我們把這種拷貝稱為淺拷貝。相對地,如果拷貝構(gòu)造函數(shù)不僅把成員數(shù)據(jù)拷貝過來,連動態(tài)申請的資源也拷貝過來,我們則稱之為深拷貝。

下面我們來看如何實現(xiàn)深拷貝:(程序15.4.2)
//node.h同程序15.4.1
//linklist.h
#include "node.h"//需要使用鏈表結(jié)點類
#include <iostream>
using namespace std; 
class Linklist
{
   public:
   Linklist(int i,char c);//鏈表類構(gòu)造函數(shù)
   Linklist(Linklist &l);//鏈表深拷貝構(gòu)造函數(shù)
   bool Locate(int i);//根據(jù)整數(shù)查找結(jié)點
   bool Locate(char c);//根據(jù)字符查找結(jié)點
   bool Insert(int i=0,char c='0');//在當前結(jié)點之后插入結(jié)點
   bool Delete();//刪除當前結(jié)點
   void Show();//顯示鏈表所有數(shù)據(jù)
   void Destroy();//清除整個鏈表
   private:
   Node head;//頭結(jié)點
   Node * pcurrent;//當前結(jié)點指針
};
//未定義的函數(shù)與程序15.3相同
Linklist::Linklist(Linklist &l):head(l.head)
{
   cout<<"Linklist Deep cloner running..." <<endl;
   pcurrent=&head;
   Node * ptemp1=l.head.readn();//該指針用于指向原鏈表中被復制的結(jié)點
   while(ptemp1!=NULL)
   {
      Node * ptemp2=new Node(ptemp1->readi(),ptemp1->readc(),pcurrent,NULL);//新建結(jié)點,并復制idata和cdata,思考為何這里不能直接用Node的拷貝構(gòu)造函數(shù)?
      pcurrent->setn(ptemp2);
      pcurrent=pcurrent->readn();//指向表尾結(jié)點
      ptemp1=ptemp1->readn();//指向下一個被復制結(jié)點
   }
}
//main.cpp
#include "Linklist.h"
#include <iostream>
using namespace std;
int main()
{
   int tempi;
   char tempc;
   cout <<"請輸入一個整數(shù)和一個字符:" <<endl;
   cin >>tempi >>tempc;
   Linklist a(tempi,tempc);
   a.Locate(tempi);
   a.Insert(1,'C');
   a.Insert(2,'B'); 
   a.Insert(3,'F');
   cout <<"After Insert" <<endl;
   a.Show();
   a.Locate('B');
   a.Delete();
   cout <<"After Delete" <<endl;
   a.Show();
   Linklist b(a);//創(chuàng)建一個鏈表b,并且將鏈表a復制到鏈表b
   cout <<"This is Linklist b" <<endl;
   b.Show();
   a.Destroy();
   cout <<"After Destroy" <<endl;
   a.Show();
   cout <<"This is Linklist b" <<endl;
   b.Show();//鏈表a被Destroy之后察看鏈表b的內(nèi)容
   return 0;
}

運行結(jié)果:
請輸入一個整數(shù)和一個字符:
4 G
Node constructor is running...
Linklist constructor is running...
Node constructor is running...
Node constructor is running...
Node constructor is running...
After Insert
4 G
3 F
2 B
1 C
After Delete
4 G
3 F
1 C
Linklist Deep cloner running...
Node constructor is running...
Node constructor is running...
This is Linklist b
4 G
3 F
1 C
After Destroy
4 G
This is Linklist b
4 G
3 F
1 C
我們看到,現(xiàn)在即使運行a.Destroy()之后,鏈表b里面的數(shù)據(jù)仍然能夠正常顯示。這是因為深拷貝構(gòu)造函數(shù)是真正意義上的復制了鏈表a,并且使得鏈表a和鏈表b各自獨立,互不干擾。這才是自定義拷貝構(gòu)造函數(shù)存在的重要意義。
發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 丰顺县| 高台县| 屯昌县| 阿坝县| 枞阳县| 天峻县| 高要市| 瑞昌市| 旬邑县| 五常市| 康乐县| 满洲里市| 内江市| 顺平县| 泗阳县| 金乡县| 赫章县| 四子王旗| 门头沟区| 黄骅市| 鄱阳县| 泰顺县| 衡东县| 页游| 弥渡县| 广州市| 杭州市| 崇信县| 梅河口市| 浙江省| 平和县| 安徽省| 邵东县| 广平县| 广州市| 武冈市| 神木县| 阜阳市| 天水市| 古蔺县| 白沙|