初學C++——鄭莉老師的課 第四章 建構函式/委託建構函式/拷貝建構函式(深拷貝/淺拷貝)
建構函式——用於初始化物件
函式名與類名相同,不能有返回值型別,可以有形式引數,也可以沒有形式引數,可以是inline函式,可以過載,可以帶預設引數值。
在物件建立時自動呼叫。如: Clock myClock(0,0,0);
預設建構函式(default constructor):呼叫時可以不需要實參的建構函式。有以下兩種:
1. 引數表為空的建構函式
2. 全部引數都有預設值的建構函式。
clcok();
clock(int newH=0, int newM=0, int newS=0);
//這兩個都是預設建構函式,如果在類內同時出現,將會產生編譯錯誤
如果程式中已經定義了建構函式,預設情況下編譯器不再隱含生成建構函式,如果此時希望那個編譯器隱含生成建構函式可以使用“=default”
class Clock
{
public:
Clock() = default;//指示編譯器依然提供預設建構函式
Clock(int newH, int newM, int newS); //建構函式
private:
int hour,minute,second;
};
建構函式的例項:
#include<iostream> using namespace std; class Clock { public: Clock(int newH, int newM, int newS);//建構函式 void setTime(int newH, int newM, int newS);//當呼叫這個函式時,如果給了實參 //就用實參值,如果沒給,就用預設引數值(0,0,0) void showTime(); private: int hour,minute,second; }; //建構函式的實現,優先選初始化列表的方式初始化 Clock::Clock(int newH,int newM,int newS):hour(newH),minute(newM),second(newS){} int main() { Clock c(0, 0, 0);//自動呼叫建構函式 c.showTime(); return 0; }
委託建構函式:在一個類中過載多個建構函式時,這些函式只是形參不同,初始化列表不同,而初始化演算法和函式體都是相同的。這個時候,為了避免重複,C++11新標準提供了委託建構函式。更重要的是,可以保持程式碼的一致性,如果以後要修改建構函式的程式碼,只需要在一處修改即可。
Clock::Clock(int newH, int newM, int newS)::hour(newH), minute(newM), second(newS) {}//建構函式
Clock::Clock():hour(0),minute(0),second(0){}//預設建構函式
改為:
Clock::Clock(int newH, int newM, int newS)::hour(newH), minute(newM), second(newS) {}//建構函式 Clock::Clock(0, 0, 0) {}//預設建構函式
拷貝建構函式
- 拷貝建構函式是一種特殊的建構函式,其形參為本類物件的引用。
- 作用:用一個已經存在的物件去初始化同類型的新物件。
class 類名
{
public:
類名(形參);//建構函式
類名(const 類名 &物件名); //拷貝建構函式
//引數前用const限定,因為傳遞引用作為引數時,實際上是可以雙向傳遞資料,
//也就是說接受這個引用引數的函式,如果在函式體中對這個引用做了任何修改,那麼實參也會被同步修改,
//顯然不是拷貝建構函式的目的,
//所以,加一個const關鍵字,說明這個引用是一個常引用,只能用這個引用讀取它裡面的資料,不能用這個引用對它指向的物件進行修改。
//這樣既能夠傳引數進來,又能保證實參的安全性
};
類名::類(const 類名 &物件名)//拷貝建構函式的實現,不允許定義返回值
{ 函式體 } // 函式體中不允許有return語句。
三種典型情況需要呼叫拷貝建構函式:
- 定義一個物件時,用本類的另一個物件作為初始值,發生拷貝構造;
- 如果函式的形參是類的物件,呼叫函式時,將使用實參物件初始化形參物件,發生拷貝構造;
- 如果函式的返回值是類的物件時,函式執行完成返回主函式時,將使用return語句中的物件初始化一個臨時無名物件,傳遞給主調函式,此時發生拷貝構造。
隱含的拷貝建構函式:(寫簡單程式時,預設的拷貝建構函式就夠用挺好用的)
- 當程式設計師餓沒有定義拷貝建構函式,編譯器會為我們生成一個預設的拷貝建構函式。
- 它執行的功能:用初始值物件的每個資料成員,初始化將要建立的物件的對應資料成員。
(當類的成員有指標時,淺拷貝就不夠用了,就要用到深拷貝了。)
當物件不想被複制構造時,用“=delete"只是編譯器不生成預設的拷貝建構函式。
class Point //Point類定義
{
public: //外部介面
Point(int x = 0, int y = 0) :x(x), y(y) {} //建構函式
Point(const Point &p)=delete;//指示編譯器不生成預設拷貝建構函式
int getX() { return x; }
int getY() { return y; }
private:
}
例子:
#include<iostream>
#include<cmath>
using namespace std;
class Point //Point類定義
{
public: //外部介面
Point(int x = 0, int y = 0) :x(x), y(y) {} //建構函式
Point(const Point &p);
int getX() { return x; }
int getY() { return y; }
private: //私有資料成員
int x, y;
};
//成員函式的實現
Point::Point(const Point &p)
{
x = p.x;
y = p.y;
cout << "calling the copy constructor" << endl; //作為例題,看到流程經過了哪些地方,也叫除錯資訊。
}
//形參為Point類物件的函式
void fun1(Point p)
{
cout << p.getX() << endl;
}
//返回值為Point類物件的函式
Point fun2()
{
Point a;
return a;
}
int main()
{
Point a;
Point b(a);//情況一:用物件a初始化物件b,第一次呼叫拷貝建構函式
cout << b.getX() << endl;
fun1(b);//情況二:物件b作為fun1的實參,第二次呼叫拷貝建構函式。先呼叫Point類的拷貝建構函式,再呼叫fun1()
b = fun2(); //情況三,函式返回值為類物件,函式返回時,即在呼叫fun2(),執行到返回a這條語句時,
//第三次呼叫拷貝建構函式。而給b賦值時,不會呼叫拷貝建構函式
//賦值實際上隱含一個賦值運算子函式來完成的。
cout << b.getX() << endl;
while (1);
return 0;
}
深拷貝/淺拷貝
當類的資料成員有指標或者為有動態成員存在時,而指標指向的空間是在構造物件時通過動態分配記憶體獲得的空間。這時,要對物件進行拷貝構造,還能用淺拷貝嗎?(不可以)
#include<iostream>
using namespace std;
class Rect
{
public:
Rect()
{
count++;
}
~Rect()
{
count--;
}
static int getCount()
{
return count;
}
private:
int width;
int height;
static int count;
};
int Rect::count=0;
int main()
{
Rect rect1;
cout<<"The count of Rect:"<<Rect::getCount()<<endl;
Rect rect2(rect1);
cout<<"The count of Rect:"<<Rect::getCount()<<endl;
return 0;
}
按道理來說,此時應該有兩個物件,但是結果顯示只有一個物件,其實,就是預設拷貝建構函式沒有處理靜態資料成員。
#include<iostream>
using namespace std;
class Rect
{
public:
Rect()
{
count++;
}
Rect(const Rect& r) //自己寫的拷貝建構函式
{
width=r.width;
height=r.height;
count++;
}
~Rect()
{
count--;
}
static int getCount()
{
return count;
}
private:
int width;
int height;
static int count;
};
int Rect::count=0;
int main()
{
Rect rect1;
cout<<"The count of Rect:"<<Rect::getCount()<<endl;
Rect rect2(rect1);
cout<<"The count of Rect:"<<Rect::getCount()<<endl;
return 0;
}
所謂淺拷貝,指的是在物件複製時,只對物件中的資料成員進行簡單的複製,預設拷貝函式執行的就是淺拷貝,多數情況,淺拷貝可以很好完成工作了,但一旦物件中存在動態成員,那麼就得用深拷貝了。比如以下:
- 淺拷貝(預設拷貝建構函式)
#include<iostream>
#include<assert.h>
using namespace std;
class Rect
{
public:
Rect()
{
p=new int(100); //動態獲取記憶體
}
~Rect()
{
assert(p!=NULL);
delete p;
}
private:
int width;
int height;
int *p;
};
int main()
{
Rect rect1;
Rect rect2(rect1);
return 0;
}
程式執行時,報錯:
出錯的原因是在進行復制時候,對於動態分配的內容沒有進行正確的操作。
即在淺拷貝時,將兩個指標指向同一個空間,但是在delete銷燬物件時,該空間會被釋放兩次,所以程式就報錯了。在此,我們需要的不是兩個指標有相同的值,而是兩個指向不同地址空間相同的值,也就是要用到“深拷貝”。
- 深拷貝
對於物件中的動態成員,就不能簡單賦值了,而是要重新動態分配空間,改一下上面的程式程式碼。
#include<iostream>
#include<assert.h>
using namespace std;
class Rect
{
public:
Rect()
{
p=new int(100);
}
Rect(const Rect& r)//新增的拷貝建構函式
{
width=r.width;
height=r.height;
p=new int(100);
*p=*(r.p);
}
~Rect()
{
assert(p!=NULL);
delete p;
}
private:
int width;
int height;
int *p;
};
int main()
{
Rect rect1;
Rect rect2(rect1);
return 0;
}
成功執行。
深拷貝和淺拷貝的區別在於:深拷貝會在堆記憶體中另外
相關推薦
初學C++——鄭莉老師的課 第四章 建構函式/委託建構函式/拷貝建構函式(深拷貝/淺拷貝)
建構函式——用於初始化物件函式名與類名相同,不能有返回值型別,可以有形式引數,也可以沒有形式引數,可以是inline函式,可以過載,可以帶預設引數值。在物件建立時自動呼叫。如: Clock myClock(0,0,0);預設建構函式(default constructor):
the c programming language second edition 第四章函式與程式結構筆記及練習題中
the c programming language second edition 第四章函式與程式結構筆記 4.3外部變數 C語言程式可以看成由一系列的外部物件構成,這些外部物件可能是變數或函式 外部變數和函式具有以下性質:通過同一個名字對外部變數的所有引
the c programming language second edition 第四章函式與程式結構筆記及練習題上
the c programming language second edition 第四章函式與程式結構筆記 4.1函式的基本認識 編寫一個程式它將輸入中包含特定模式或字串的各行打印出來。 該任務可以明確地劃分成下列3部分: while(未處理的行) if
C++ primer 學習筆記(到第四章)
1、計算機內部實現過程有別,詳細如下:<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /> i=i+1的過程相當: temp=i+1; i=temp; i++的過程相當: temp=i;
C語言程式設計入門之--第四章C語言基本資料型別
導讀:C語言程式中經常涉及一些數學計算,所以要熟悉其基本的資料型別。資料型別學習起來比較枯燥,不過結合之前的記憶體概念,以及本節的位元組概念,相信資料型別也就不難理解了。本章從二進位制的基本概念開始,然後介紹機器語言通用的計算單位位元組,最後再介紹C語言中基本的資料型別及其基本概念。 &
程式設計師程式設計藝術-----第四章-----現場編寫類似strstr/strcpy/strpbrk的函式
第四章、現場編寫類似strstr/strcpy/strpbrk的函式 作者:July。 說明: 如果在部落格中程式碼使用了\n,csdn blog系統將會自動回給我變成/n。據後續驗證,可能是原來舊blog版本的bug,新版已不存在此問題。至於,本文程式碼,日後統一修正
Elixir超程式設計-第六章 能力越大,責任也越大(樂趣也越大)
Elixir超程式設計-第六章 能力越大,責任也越大(樂趣也越大) 我們已經揭開了 Elixir 超程式設計的神祕面紗。我們從基礎
C++對象模型——Inline Functions(第四章)
優化 tor tracking 改善 pan c++ col ria 表達式 4.5 Inline Functions 以下是Point class 的一個加法運算符的可能實現內容: class Point { friend Point operato
OO先導課——第四次上課
style 結果 內容 blog alt 測試性能 img log ges 上課內容:測試正確率;測試性能 結果: 教訓:能用類庫的就用類庫!自己寫的很難快起來!OO先導課——第四次上課
c++primer 第四章編程練習答案
float enter put rand out har lin score ring 4.13.1 #include<iostream> struct students { char firstname[20]; char lastname
【C++ 第四章 個人銀行賬戶管理程序案例】
with count acc cpp name c++ money 建立 esc 【第四章】 個人銀行賬戶管理程序 案例實現 #include<iostream> #include<cmath> using namespace std; clas
《C++程序設計語言(英文第四版)》【PDF】下載
files com gpo spa stat per const read 編程 《C++程序設計語言(英文第四版)》【PDF】下載鏈接: https://u253469.pipipan.com/fs/253469-230382177 內容簡介 本書是C++領域經
c語言第四章-條件結構
case 數值 運算符和 替代 替代品 OS 運算符 結構 div 表達式:由運算符和操作數組成賦值運算符:=多分枝選擇結構if else if else if else嵌套結構if(){ if(){ }}switch case 結構defaul
第五課-第四講05_04_bash腳本編程之三 條件判斷及算術運算
ash 如果 寫一個腳本 字符 命令引用 是否 練習 bash腳本 [] 第五課-第四講05_04_bash腳本編程之三 條件判斷及算術運算 練習:寫一個腳本,判斷當前系統上是否有用戶的默認shell為bash:如果有,就顯示有多少個這類用戶,否則,就顯示沒有這類用戶 bc
第七課-第四講 07_04_特殊權限SUID等詳解
人的 大寫 backup 就是 取值 特殊權限 詳解 -- 執行 第七課-第四講 07_04_特殊權限SUID等詳解 一. 特殊權限 SUID: 運行某程序時,相應進程的屬主是該程序文件自身的屬主,而不是啟動者chmod u+s filechmod u-s file文件本
C語言每日一練——第四題
圖片 數據文件 () inf fop dat文件 print 數組a open 一、題目要求 已知數據文件in.dat中有300個四位數,並調用readDat()函數把這些數存儲數組a中,編寫函數jsValue(),其功能是:求出所有這些四位數是素數的個數cnt,再把所有滿
CLR via C#學習筆記-第四章-類型基礎-命名空間和程序集
程序集 microsoft 集中 歧義 str 可能 ring 需要 idg 4.3 命名空間和程序集 使用using指令簡化命名空間 C#編譯器通過using指令提供這個機制,例如 using System.IO; using System.Text; 只需要在代碼中
CLR via C#學習筆記-第四章-類型基礎-所有類型都從System.Object派生
回收 spa 操作 哈希 包括 生成 自動生成 返回 equals 4.1 所有類型都從System.Object派生 System.Object類型 運行時要求每個類型最終都從System.Object類型派生 也就是說,以下兩個類型定義完全一樣 //隱式派生自Syst
CLR via C#學習筆記-第四章-類型基礎-運行時的相互關系
分配內存 ring type類 實現 語句 初始化 sem strong 允許 4.4 運行時的相互關系 已加載CLR的一個Windows進程,該進程可能有多個線程。線程創建時會分到1MB的棧。棧空間用於向方法傳遞實參,方法內部定義的局部變量也在棧上。 以下是方法M1和M2
C#本質論6.0第四章:方法和參數
有助於 異常 名稱 別名 不同的 元素 寫入 轉換 參數順序 方法和參數 方法組合一系列語句以執行特定操作或計算特定結果,它能夠為構成程序的語句提供更好的結構和組織。 方法總是和類型——通常是類關聯。 方法通過實參接收數據,實參由方法的參數或形參定義,參數是調用者用於向被