C++: 複製建構函式
C++複製建構函式
標籤(空格分隔): C++
我們經常會需要用一個也已經存在的物件,去初始化新的物件,這時就需要一種特殊的建構函式——複製建構函式;
預設的複製建構函式可以實現對應資料成員一一複製;
複製建構函式定義
複製建構函式是一種特殊的建構函式,其形參為本類的物件引用。作用是用一個已存在的物件去初始化同類型的新物件。
class 類名 {
public :
類名(形參);//建構函式
類名(const 類名 &物件名);//複製建構函式
// ...
};
類名::類( const 類名 &物件名)//複製建構函式的實現
{ 函式體 }
隱含的複製建構函式
- 如果程式設計師沒有為類宣告拷貝初始化建構函式,則編譯器自己生成一個隱含的複製建構函式。
- 這個建構函式執行的功能是:用作為初始值的物件的每個資料成員的值,初始化將要建立的物件的對應資料成員。
“=delete”
如果不希望物件被複制構造
▫ C++98做法:將複製建構函式宣告為private,並且不提供函式的實現。
▫ C++11做法:用“=delete”指示編譯器不生成預設複製建構函式。
例:
class Point { //Point 類的定義
public:
Point(int xx=0, int yy=0) { x = xx; y = yy; } //建構函式,內聯
Point(const Point& p) =delete; //指示編譯器不生成預設複製建構函式
private:
int x, y; //私有資料
};
複製建構函式被呼叫的三種情況
- 定義一個物件時,以本類另一個物件作為初始值,發生複製構造;
- 如果函式的形參是類的物件,呼叫函式時,將使用實參物件初始化形參物件,發生複製構造;
- 如果函式的返回值是類的物件,函式執行完成返回主調函式時,將使用return語句中的物件初始化一個臨時無名物件,傳遞給主調函式,此時發生複製構造。
定義一個物件時,以本類另一個物件作為初始值,發生複製構造
class Point{
public :
Point(int a, int b){x=a; y=b;} // 建構函式
Point(const Point & p); // 複製建構函式
void setX(int a){ x = a; }
void setY(int b){ y = b; }
int getX(){ return x; }
int getY(){ return y; }
private:
int x,y;
};
// 複製建構函式的實現
Point::Point(const Point &p){
x = p.x;
y = p.y;
}
// 主函式
int main(int argc, char** argv) {
Point point1(2,3);
cout << point1.getX() << endl;
Point point2(point1);
cout << point2.getX() << endl;
return 0;
}
物件以值傳遞的方式傳入函式引數
class Point{
public:
Point(int a, int b){x=a; y=b;} // 建構函式
Point(const Point & p); //複製建構函式
void setX(int a){ x = a; }
void setY(int b){ y = b; }
int getX(){ return x; }
int getY(){ return y; }
private:
int x,y;
};
// 複製建構函式的實現
Point::Point(const Point &p){
x = p.x;
y = p.y;
}
// 形參為Point類物件void
void function1(Point p){
cout << p.getY() << endl;
}
// 主函式
int main(int argc, char** argv) {
Point point1(2,3);
function1(point1);
return 0;
}
物件以值傳遞的方式從函式返回
class Point{
public:
Point(int a, int b){x=a; y=b;} // 建構函式
Point(const Point & p); //複製建構函式
void setX(int a){ x = a; }
void setY(int b){ y = b; }
int getX(){ return x; }
int getY(){ return y; }
private:
int x,y;
};
// 複製建構函式的實現
Point::Point(const Point &p){
x = p.x;
y = p.y;
}
//返回值為Point類物件
Point function2(){
Point a(4, 5);
return a;
}
// 主函式
int main(int argc, char** argv) {
Point c = function2();
cout << c.getY() << endl;
return 0;
}
淺拷貝和深拷貝
預設拷貝建構函式
很多時候在我們都不知道拷貝建構函式的情況下,傳遞物件給函式引數或者函式返回物件都能很好的進行,這是因為編譯器會給我們自動產生一個拷貝建構函式,這就是“預設拷貝建構函式”,這個建構函式很簡單,僅僅使用“老物件”的資料成員的值對“新物件”的資料成員一一進行賦值,它一般具有以下形式:
Point(const Point & p){
x = p.x;
y = p.y;
}
當然,以上程式碼不用我們編寫,編譯器會為我們自動生成。
但是問題來了,請看下面問題:
class Point{
public:
Point(int a, int b); // 建構函式
~Point(); // 解構函式
Point(const Point & p); //複製建構函式
static int showCount(); // 顯示count數
private:
int x,y;
static int count;
};
// 建構函式
Point::Point(int a, int b){
x = a;
y = b;
count++;
}
// 解構函式
Point::~Point(){
count--;
}
// 複製建構函式的實現
Point::Point(const Point &p){
x = p.x;
y = p.y;
}
// 顯示count數
int Point::showCount(){
return count;
}
// 初始化計數器
int Point::count = 0;
// 主函式
int main(int argc, char** argv) {
Point point1(2,3);
cout << "count=" << Point::showCount() << endl;
Point point2(point1);
cout << "count=" << Point::showCount() << endl;
return 0;
}
這段程式碼對前面的類,加入了一個靜態成員,目的是進行計數。在主函式中,首先建立物件point1,輸出此時的物件個數,然後使用rect1複製出物件point2,再輸出此時的物件個數,按照理解,此時應該有兩個物件存在,但實際程式執行時,輸出的都是1,反應出只有1個物件。此外,在銷燬物件時,由於會呼叫銷燬兩個物件,類的解構函式會呼叫兩次,此時的計數器將變為負數。
問題來了:拷貝建構函式沒有處理靜態資料成員
重新編寫程式碼如下:
class Point{
public:
Point(int a, int b); // 建構函式
~Point(); // 解構函式
Point(const Point & p); //複製建構函式
static int showCount(); // 顯示count數
private:
int x,y;
static int count;
};
// 建構函式
Point::Point(int a, int b){
x = a;
y = b;
count++;
}
// 解構函式
Point::~Point(){
count--;
}
// 複製建構函式的實現
Point::Point(const Point &p){
x = p.x;
y = p.y;
count++;
}
// 顯示count數
int Point::showCount(){
return count;
}
// 初始化計數器
int Point::count = 0;
// 主函式
int main(int argc, char** argv) {
Point point1(2,3);
cout << "count=" << Point::showCount() << endl;
Point point2(point1);
cout << "count=" << Point::showCount() << endl;
return 0;
}
問題解決
淺拷貝
所謂淺拷貝,指的是在物件複製時,只對物件中的資料成員進行簡單的賦值,預設拷貝建構函式執行的也是淺拷貝。大多情況下“淺拷貝”已經能很好地工作了,但是一旦物件存在了動態成員,那麼淺拷貝就會出問題了,讓我們考慮如下一段程式碼:
class Point{
public:
Point(int a, int b); // 建構函式
~Point(); // 解構函式
private:
int x,y;
int* p;
};
// 建構函式
Point::Point(int a, int b){
x = a;
y = b;
p = new int(100);
}
// 解構函式
Point::~Point(){
if(p != NULL){
delete p;
}
}
// 主函式
int main(int argc, char** argv) {
Point point1(2,3);
Point point2(point1);
return 0;
}
在這段程式碼執行結束之前,會出現一個執行錯誤。原因就在於在進行物件複製時,對於動態分配的內容沒有進行正確的操作。我們來分析一下:
在執行定義point1物件後,由於在建構函式中有一個動態分配的語句,因此執行後的記憶體情況大致如下:
在使用point1複製point2時,由於執行的是淺拷貝,只是將成員的值進行賦值,這時 point1.p = point2.p,也即這兩個指標指向了堆裡的同一個空間,如下圖所示:
當然,這不是我們所期望的結果,在銷燬物件時,兩個物件的解構函式將對同一個記憶體空間釋放兩次,這就是錯誤出現的原因。我們需要的不是兩個p有相同的值,而是兩個p指向的空間有相同的值,解決辦法就是使用“深拷貝”。
深拷貝
在“深拷貝”的情況下,對於物件中動態成員,就不能僅僅簡單地賦值了,而應該重新動態分配空間,如上面的例子就應該按照如下的方式進行處理:
class Point{
public:
Point(int a, int b); // 建構函式
~Point(); // 解構函式
Point(const Point & p); //複製建構函式
private:
int x,y;
int* p;
};
// 建構函式
Point::Point(int a, int b){
x = a;
y = b;
p = new int(100);
}
// 解構函式
Point::~Point(){
if(p != NULL){
delete p;
}
}
// 複製建構函式的實現
Point::Point(const Point &q){
x = q.x;
y = q.y;
p = new int; // 為新物件重新動態分配空間
*p = *(q.p);
}
// 主函式
int main(int argc, char** argv) {
Point point1(2,3);
Point point2(point1);
return 0;
}
此時,在完成物件的複製後,記憶體的一個大致情況如下:
此時point1的p和point22的p各自指向一段記憶體空間,但它們指向的空間具有相同的內容,這就是所謂的“深拷貝”。
相關推薦
C++ 複製建構函式和運算子過載示例
string1.h // // Created by lance on 10/16/18. // #ifndef CPP_PRIMER_STRING1_H #define CPP_PRIMER_STRING1_H #include <iostream> u
C++複製建構函式引數問題
今天遇到一個題, #include <iostream> using namespace std; class Sample { public: int v; // 在此處補充你的程式碼 }; void PrintAndDouble(Sample o) { c
C++ 複製建構函式和賦值運算子過載函式
宣告一個空的類testsize,sizeof(testsize)為1,為其宣告建構函式和解構函式,依舊為1 建構函式不能使用關鍵字virtual,解構函式可以 一旦類中存在虛擬函式,就會為該類生成虛擬函式表,並在每一個例項中新增一個指向虛擬函式表的指標,從而大小為一個指標大
C++複製建構函式&移動建構函式,複製賦值運算子&移動賦值運算子
一、呼叫時機 1、複製建構函式呼叫的時機 ·物件在建立時使用其他的物件初始化 Person p(q); //此時複製建構函式被用來建立例項p Person p = q; //此時複製建構函式被用來在定義例項p時初始化p return_p() //當函式返回該型別的物件
C++----複製建構函式(拷貝建構函式)--- 轉自CSDN
向作者致敬!也許很多C++的初學者都知道什麼是建構函式,但是對複製建構函式(copy constructor)卻還很陌生。對於我來說,在寫程式碼的時候能用得上覆制建構函式的機會並不多,不過這並不說明覆制建構函式沒什麼用,其實複製建構函式能解決一些我們常常會忽略的問題。 為了說明覆制建構函式作用,
C++: 複製建構函式
C++複製建構函式 標籤(空格分隔): C++ 我們經常會需要用一個也已經存在的物件,去初始化新的物件,這時就需要一種特殊的建構函式——複製建構函式; 預設的複製建構函式可以實現對應資料成員一一複製; 複製建構函式定義 複製建構函式是一種特殊的
C++複製建構函式,過載賦值運算子
C++的複製建構函式, 賦值建構函式, 有時候會有點暈,下面總結一下: 首先來談一下複製建構函式: 程式碼: #include<iostream> using namespace std; #include<cstring> #include<
C++複製建構函式與過載賦值操作符
內容整理自: 函式原型 在C++中建立一個類,這個類中肯定會包括建構函式、解構函式、複製建構函式和過載賦值操作。 複製建構函式是一種特殊的建構函式,其作用也是為類的成員初始化以及為物件的構造分配儲存空間。函式的名稱必須和類名稱一致,無返回型別,它的唯一的一個引數
c++ 複製建構函式示例程式
/******************************************************* *author:彭曉林 *copyright: 版權所有,翻版不究 *function: 複製建構函式測試程式 *************************
沒想通的關於複製建構函式C++程式碼: A obj=func()
class A { public: A(int x=0):i(x) { cout << "Normal Contructor: " << i << endl; } ~A() {
關於複製建構函式及關於類的簡單函式使用(C++)
剛學C++,關於複製建構函式的思考: 關於複製建構函式 : 相當於在記憶體新建一個類,再把資料複製給目標。可以是不完全複製(也是其存在意義),比如資料加減等。 三種使用情景: 1.用已知物件初始化信物件; 2.形參為物件; 3.返回值為物件; 簡單函式: 1.宣
【c++】=過載,報錯:沒有合適的複製建構函式
不想看太多的朋友看這句就行了:在你的複製建構函式作為引數傳進去的那個物件前加個const. 是這樣的,在寫機器學習作業的時候,遇到了很多矩陣演算法,為了處理上的方便,我寫了一個矩陣類Matrix,如下
C++中類的建構函式與複製建構函式
1 相關定義 1.1 建構函式 建構函式是類的特殊的成員函式,只要建立類型別的新物件,都要執行建構函式。建構函式的工作是保證每個物件的資料成員具有合適的初始值。建構函式的名字與類的名字相同,並且不能指定返回型別。像其他任何函式一樣,它們可以沒有形參,也可以定義多個形參。 1
C++在單繼承、多繼承、虛繼承時,建構函式、複製建構函式、賦值操作符、解構函式的執行順序和執行內容
一、本文目的與說明 1. 本文目的:理清在各種繼承時,建構函式、複製建構函式、賦值操作符、解構函式的執行順序和執行內容。 2. 說明:雖然複製建構函式屬於建構函式的一種,有共同的地方,但是也具有一定的特殊性,所以在總結它的性質時將它單獨列出來了。
C++結構體:預設建構函式,複製建構函式,過載=運算子
C++結構體提供了比C結構體更多的功能,如預設建構函式,複製建構函式,運算子過載,這些功能使得結構體物件能夠方便的傳值。 比如,我定義一個簡單的結構體,然後將其作為vector元素型別,要使用的話,就需要實現上述三個函式,否則就只能用指標了。 #include
c++實現String類(建構函式,解構函式,複製建構函式,各類運算子過載函式的編寫)
編寫類 String 的建構函式,解構函式,複製建構函式 需要過載下面的運算子: 1、<、>、==和!=比較運算子 2、+=連線運算子和賦值運算子 3、<<輸出運算子和>>輸入運算子 String.h #ifndef _STRING
google C++ 程式設計規範中的禁用複製建構函式和賦值運算子
在google C++程式設計規範中有下面一段描述: 僅在程式碼中需要拷貝一個類物件的時候使用拷貝建構函式;不需要拷貝時應使用 DISALLOW_COPY_AND_ASSIGN。 定義:通過拷貝新建物件時可使用拷貝建構函式(特別是物件的傳值時)。 優點:拷貝建
C++語法,複製建構函式與=運算子過載
1、物件在建立時使用其他的物件初始化 Person p(q); //此時複製建構函式被用來建立例項p Person p = q; //此時複製建構函式被用來在定義例項p時初始化p 2、物件作為函式的引數進行值傳遞時 f(p); //此時p作為函式的引數進行值傳遞,p入棧時會呼叫複製建構函式建立一
C++基礎知識(八)---函式返回值(返回值,返回指標,返回物件,返回引用)---引用---複製建構函式(拷貝建構函式)
一、函式返回值 1.返回值: int test () { int a=1; return a; } 返回值時最簡單的方式,它的操作主要在棧上,變數a在函式結束後會刪除,為了返回a的值,系統會在內部建立一個臨時變數儲存a的值,以返回給呼叫該函式的表示式,呼叫結束後變數便
【C++】三種呼叫類的複製建構函式的情況
用類的一個物件初始化同類的另一個物件時。 某函式的返回值是類的物件,呼叫該函式時。 某函式的形參是類的物件,呼叫該函式時。 ※注意區分“初始化”和“賦值”: ClassName c2 = c1; (初始化語句) ClassName c1 , c2;