日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

拷贝构造函数编程实验

發布時間:2024/4/18 编程问答 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 拷贝构造函数编程实验 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

拷貝構造函數編程實驗

    • 基本知識
      • 拷貝控制函數
      • 拷貝構造函數
      • 析構函數
      • 關于本實驗
    • 示例代碼
      • lab12_1.cpp
      • lab12_2.cpp
      • lab12_3.cpp
      • lab12_4.cpp
      • lab12_5.cpp

本文是本人大一期間在校學習C++課程時所撰寫的實驗報告的摘錄,由于剛上大學,剛接觸計算機編程方面的相關知識,故可能會有很多不足甚至錯誤的地方,還請各位看官指出及糾正。
本文所涉及到的“教材”指:
電子工業出版社《C++ Primary中文版(第5版)》
如需轉載或引用請標明出處。

基本知識

拷貝控制函數

當定義一個類時,我們顯示地或隱式地指定在此類型的對象拷貝、移動、賦值和銷毀時做什么,而負責控制這些操作的函數我們稱為拷貝控制成員,把這些操作稱為拷貝控制操作。

如果一個類沒有定義所有的這些拷貝控制成員,編譯器會自動為它定義缺失的操作。因此,很多類會忽略這些拷貝控制操作。但是,對一些類來說,依賴這些操作的默認定義會導致嚴重的后果。因此,學會判斷什么時候需要定義它們,如何定義他們是非常重要的。

拷貝構造函數

拷貝構造函數是拷貝控制成員的其中之一。如果一個構造函數的第一個參數是自身類類型的引用,且任何額外參數都有默認值,則此構造函數是拷貝構造函數。例如:

class Foo { public:Foo(); //默認構造函數Foo(const Foo&); //拷貝構造函數//... };

由于拷貝構造函數被用來初始化非引用類類型參數,因此拷貝構造函數的第一個參數必須是一個引用類型。

如果我們沒有為一個類定義拷貝構造函數,編譯器會為我們定義一個。與合成默認構造函數不同,即使我們定義了其他構造函數,編譯器其也會為我們合成一個拷貝構造函數。一般情況下,合成的拷貝構造函數會將其參數的成員逐個拷貝到正在創建的對象中,且每個成員的類型決定了它如何拷貝。

一般而言,在執行拷貝初始化時,會調用拷貝構造函數或移動構造函數中的一種。而在下列情況中將會執行拷貝初始化:

  • 使用=定義變量時
  • 將一個對象作為實參傳遞給一個非引用類型的形參時
  • 從一個返回類型為非引用類型的函數返回一個對象時
  • 用花括號列表初始化一個數組中的元素或一個聚合類中的成員時
  • 某些類類型還會對它們所分配的對象使用拷貝初始化(例如標準庫容器中的insert和push成員函數)

析構函數

析構函數是拷貝控制成員的其中之一。析構函數執行與構造函數相反的操作,析構函數釋放對象使用的資源,并銷毀對象的非static數據成員。

析構函數是類的一個成員函數,名字由波浪號接類名構成。它沒有返回值,也不接受參數。例如:

class Foo { public:~Foo(); //析構函數//... };

由于析構函數不接受參數,因此它不能被重載,對于一個給定的類,也只有唯一一個析構函數。

析構函數有一個函數體和一個析構部分,在一個析構函數中,首先執行函數體,再銷毀成員,成員按初始化順序的逆序銷毀。類似的,當一個類未定義自己析構函數時,編譯器會為它定義一個合成析構函數。

一般而言,在下列情況中,析構函數會被調用:

  • 變量離開其作用域時
  • 當一個對象被銷毀時,其成員被銷毀
  • 容器被銷毀時,其元素被銷毀
  • 對于動態分配的對象,當對指向它的指針應用delete運算符時
  • 對于臨時對象,當創建它的完整表達式結束時

需要注意的是,成員是在析構函數體之后隱含的析構階段中被銷毀的,在整個對象的銷毀過程中,析構函數體是作為成員銷毀步驟之外的另一部分而進行的。

關于本實驗

在本次實驗中,共有5個程序,可以分為兩組,前三個一組與后兩個一組。通過分析代碼,對每組之間的各個程序進行對比,分析運行結果,展示了拷貝構造函數和析構函數的定義和使用方法,以及展示了它們在什么情況下會被調用。具體說明詳見代碼注釋部分。

示例代碼

lab12_1.cpp

#include<iostream> using namespace std; class CExample //測試類型 { private:int a; //唯一成員 public:CExample(int b) //構造函數:接受一個整型實參,用于初始化成員a {a = b;printf("constructor is called\n");}CExample(const CExample & c) //拷貝構造函數:將實參c的成員a拷貝過來{a = c.a;printf("copy constructor is called\n");}~CExample() //析構函數:數據銷毀將在下列函數體結束后進行{cout << "destructor is called\n";}void Show() //指示成員a的值{cout << a << endl;} }; int main(void) {CExample A(100); //執行普通的構造函數,構造A CExample B = A; //執行拷貝構造函數,構造BB.Show(); //驗證執行了拷貝構造函數system("pause");return 0; //由于此時A和B仍在自己的作用域內,故沒有執行析構函數 }

運行結果:

lab12_2.cpp

#include<iostream> using namespace std; class CExample //測試類型 { private:int a; //唯一成員 public:CExample(int b) //構造函數:接受一個整型實參,用于初始化成員a{a = b;printf("constructor is called\n");}CExample(const CExample & c) //拷貝構造函數:將實參c的成員a拷貝過來{a = c.a; printf("copy constructor is called\n");}~CExample() //析構函數:數據銷毀將在下列函數體結束后進行{cout << "destructor is called\n";}void Show() //指示成員a的值{cout << a << endl;} }; void g_fun(CExample c) //非引用類型的形參,調用時會執行一次拷貝構造函數,構造c的副本 {cout << "g_func" << endl; } int main(void) {CExample A(100); //執行普通的構造函數,構造ACExample B = A; //執行拷貝構造函數,構造BB.Show(); //驗證執行了拷貝構造函數g_fun(A); //下面是這段代碼的執行過程: //1. 執行拷貝構造函數,構造A的副本//2. 執行函數體,即輸出文本//3. 函數結束,A的副本離開了該函數的作用域,執行一次析構函數system("pause");return 0; //由于此時A和B仍在自己的作用域內,故沒有執行析構函數 }

運行結果:

lab12_3.cpp

#include<iostream> using namespace std; class CExample //測試類型 { private:int a; //唯一成員 public:CExample(int b) //構造函數:接受一個整型實參,用于初始化成員a{a = b;printf("constructor is called\n");}CExample(const CExample & c) //拷貝構造函數:將實參c的成員a拷貝過來{ a = c.a;printf("copy constructor is called\n");}~CExample() //析構函數:數據銷毀將在下列函數體結束后進行{cout << "destructor is called\n";}void Show() //指示成員a的值{cout << a << endl;} }; CExample g_fun() //測試函數 {CExample temp(0); //調用普通的構造函數,構造tempreturn temp; //返回的是非引用類型的temp,即temp的拷貝,會執行一次拷貝構造函數 } int main(void) {g_fun(); //下面是這段代碼的執行過程://1. 調用普通的構造函數,構造temp //2. 返回非引用類型的temp,即返回temp的拷貝,執行一次拷貝構造函數//3. 函數結束,temp離開其作用域,執行一次析構函數//4. 由于沒有對象承接返回的temp的拷貝,可以認為該拷貝的作用域結束,執行一次析構函數system("pause");return 0; }

運行結果:

lab12_4.cpp

#include<iostream> using namespace std; class Rect //測試類型 { public:Rect() //默認構造函數:將count的值加一{count++;}~Rect() //析構函數:先把count的值減一,再銷毀相關數據{count--;}static int getCount() //指示靜態成員count的值 {return count;} private:int width;int height; //用于說明static int count; //靜態成員count,即使多次執行拷貝構造函數,也只有一個count//即多個測試類型共享一個count }; int Rect::count = 0; //先把測試類中的count置為0 int main(void) {Rect rect1; //執行默認構造函數,構造rect1,把count的值加1cout << "The count of Rect:" << Rect::getCount() << endl; //驗證默認構造函數執行成功Rect rect2(rect1); //由于沒有自己定義拷貝構造函數,故會使用合成的拷貝構造函數去構造rect2//合成的拷貝構造函數中沒有定義count加一的操作,故count的值不變cout << "The count of Rect:" << Rect::getCount() << endl; //驗證執行的是合成的拷貝構造函數system("pause");return 0; //由于此時rect1和rect2仍在自己的作用域內,故沒有執行析構函數 }

運行結果:

lab12_5.cpp

#include<iostream> using namespace std; class Rect //測試類型 { public:Rect() //默認構造函數:將count的值加一{count++;}Rect(const Rect& r) //拷貝構造函數{width = r.width; //將實參r的成員width拷貝過來height = r.height; //將實參r的成員height拷貝過來count++; //與合成的拷貝構造函數不同的地方:將count的值加一}~Rect() //析構函數:先把count的值減一,再銷毀相關數據{count--;}static int getCount() //指示靜態成員count的值{ return count;} private:int width;int height;static int count; //靜態成員count,即使多次執行拷貝構造函數,也只有一個count }; //即多個測試類型共享一個count int Rect::count = 0; //先把測試類中的count置為0 int main(void) {Rect rect1; //執行默認構造函數,構造rect1,把count的值加1cout << "The count of Rect:" << Rect::getCount() << endl; //驗證默認構造函數執行成功Rect rect2(rect1); //執行自己定義的拷貝構造函數:構造rect2,把count的值加1cout << "The count of Rect:" << Rect::getCount() << endl; //驗證執行的是自己定義的拷貝構造函數system("pause");return 0; //由于此時rect1和rect2仍在自己的作用域內,故沒有執行析構函數 }

運行結果:

與50位技術專家面對面20年技術見證,附贈技術全景圖

總結

以上是生活随笔為你收集整理的拷贝构造函数编程实验的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。