1. 程式人生 > >C++複製建構函式與過載賦值操作符

C++複製建構函式與過載賦值操作符

內容整理自:

函式原型

在C++中建立一個類,這個類中肯定會包括建構函式、解構函式、複製建構函式和過載賦值操作。

複製建構函式是一種特殊的建構函式,其作用也是為類的成員初始化以及為物件的構造分配儲存空間。函式的名稱必須和類名稱一致,無返回型別,它的唯一的一個引數是本型別的一個引用變數,該引數是const型別,不可變。

複製建構函式原型如下:

class_name(const class_name &src);

對於一個類X, 如果一個建構函式的第一個引數是下列之一:a) & X; b) const & X; c) volatile & X; d) const volatile & X;
且沒有其他引數或其他引數都有預設值,那麼這個函式是拷貝建構函式,如下:
X::X(const & X);   
X::X(& X, int=1); 
X::X(& X, int a=1, int b=2); 

過載賦值操作符是一個特別的賦值運算子,通常是用來把已存在的物件賦值給其它相同型別的物件。

過載賦值操作符的原型如下:

class_name& operator=(const class_name &src);

複製建構函式與過載賦值操作符的呼叫

當類的物件需要拷貝時,複製建構函式將會被呼叫。以下情況都會呼叫複製建構函式:
一個物件以值傳遞的方式傳入函式體;
一個物件以值傳遞的方式從函式返回;
一個物件需要通過另外一個物件進行初始化。

如果物件在宣告的同時將另一個已存在的物件賦給它,就會呼叫複製建構函式;如果物件已經存在了,然後再將另一個已存在的物件賦給它,呼叫的就是過載賦值運算子。

#include <iostream>
using namespace std;

class CTest
{
public:
     CTest(){}
     ~CTest(){}

     CTest(const CTest &test)
     {
          cout<<"copy constructor."<<endl;
     }

     void operator=(const CTest &test)
     {
          cout<<"operator="<<endl;
     }

     void Test(CTest test)
     {}

     CTest Test2()
     {
          CTest a;
          return a;
     }

     void Test3(CTest &test)
     {}

     CTest &Test4()
     {
          CTest *pA = new CTest;
          return *pA;
     }
};

int main()
{
     CTest obj;

     CTest obj1(obj); // 呼叫複製建構函式

     obj1 = obj; // 呼叫過載賦值操作符

     /* 傳參的過程中,要呼叫一次複製建構函式
     * obj1入棧時會呼叫複製建構函式建立一個臨時物件,與函式內的區域性變數具有相同的作用域
     */
     obj.Test(obj1);

     /* 函式返回值時,呼叫複製建構函式;將返回值賦值給obj2時,呼叫過載賦值操作符
     * 函式返回值時,也會構造一個臨時物件;呼叫複製建構函式將返回值複製到臨時物件上
     */
     CTest obj2;
     obj2 = obj.Test2();

     obj2.Test3(obj); // 引數是引用,沒有呼叫複製建構函式

     CTest obj3;
     obj2.Test4(); // 返回值是引用,沒有呼叫複製建構函式

     return 0;
}

深拷貝(deep copy)與淺拷貝(shallow copy)

如果在類中沒有顯式地宣告,那麼編譯器會自動生成預設的複製建構函式和過載賦值操作符。預設的複製建構函式和賦值運算子進行的都是“shallow copy”,只是簡單地複製欄位,把值一一賦給要拷貝的值。因此如果物件中含有動態分配的記憶體,就需要我們自己重寫複製建構函式和過載賦值操作符來實現“deep copy”,確保資料的完整性和安全性。

例如:類內成員變數需要動態開闢堆記憶體,如果實行淺拷貝,也就是把物件裡的值完全複製給另一個物件,如A=B。這時,如果B中有一個成員變數指標已經申請了記憶體,那A中的那個成員變數也指向同一塊記憶體。這就出現了問題:當B把記憶體釋放了(如:析構),這時A內的指標就是野指標了,出現執行錯誤。

深拷貝和淺拷貝可以簡單理解為:如果一個類擁有資源(堆,或者是其它系統資源),當這個類的物件發生複製過程的時候,資源重新分配,使物件擁有不同的資源,但資源的內容是一樣的,這個過程就是深拷貝;反之,沒有重新分配資源,兩個物件就有用共同的資源,同時對資源可以訪問,就是淺拷貝。淺拷貝,只是對指標的拷貝,拷貝後兩個指標指向同一個記憶體空間,深拷貝不但對指標進行拷貝,而且對指標指向的內容進行拷貝,經深拷貝後的指標是指向兩個不同地址的指標。

#include <iostream>
using namespace std;
class CA
{
 public:
  CA(int b,char* cstr)
  {
   a=b;
   str=new char[b];
   strcpy(str,cstr);
  }
  CA(const CA& C)
  {
   a=C.a;
   str=new char[a]; //深拷貝
   if(str!=0)
    strcpy(str,C.str);
  }
  void Show()
  {
   cout<<str<<endl;
  }
  ~CA()
  {
   delete str;
  }
 private:
  int a;
  char *str;
};

int main()
{
 CA A(10,"Hello!");
 CA B=A;
 B.Show();
 return 0;
} 

三法則(英語:rule of three,the Law of The Big Three,The Big Three;三法則,三大定律)在 C++ 程式設計裡,它是一個以設計的基本原則而制定的定律,三法則的要求在於,假如型別有明顯地定義下列其中一個成員函式,那麼程式設計師必須連其他二個成員函式也一同編寫至型別內,亦即下列三個成員函式缺一不可:

  • 解構函式(Destructor)
  • 複製建構函式(copy constructor)
  • 複製賦值運算子(copy assignment operator)

有時候為了防止預設拷貝發生,可以將複製建構函式和過載賦值運算子設為private來禁止拷貝,並且只是宣告,不用實現。這樣的話,如果試圖呼叫 A  b(a); 就呼叫了私有的複製建構函式,編譯器會報錯。

class Widget  
{  
public:  
    int* pi;  
private:  
    Widget(const Widget&);  
    Widget& operator=(const Widget&);  
}; 

這種方法的一點小缺陷是,如果該類的成員函式或其友元函式呼叫複製建構函式或賦值操作符函式,會將錯誤推後到連線期。有一種方法可以將連線期錯誤移至編譯期:先定義一個基類,然後讓你的類繼承該類。

相關推薦

C++複製建構函式過載操作符

內容整理自: 函式原型 在C++中建立一個類,這個類中肯定會包括建構函式、解構函式、複製建構函式和過載賦值操作。 複製建構函式是一種特殊的建構函式,其作用也是為類的成員初始化以及為物件的構造分配儲存空間。函式的名稱必須和類名稱一致,無返回型別,它的唯一的一個引數

批註:C++中複製建構函式過載操作符總結:預設淺拷貝,帶指標的需要深拷貝

前言 這篇文章將對C++中複製建構函式和過載賦值操作符進行總結,包括以下內容: 複製建構函式和過載賦值操作符的定義;複製建構函式和過載賦值操作符的呼叫時機;複製建構函式和過載賦值操作符的實現要點;複製建構函式的一些細節。 複製建構函式和過載賦值操作符的定義 我們都知道

C++複製建構函式過載運算子

C++的複製建構函式, 賦值建構函式, 有時候會有點暈,下面總結一下: 首先來談一下複製建構函式: 程式碼: #include<iostream> using namespace std; #include<cstring> #include<

C++ 拷貝建構函式過載操作符不能相互呼叫

拷貝建構函式呼叫過載賦值操作符,過載賦值操作符呼叫拷貝建構函式的寫法都是沒有意義的。首先:拷貝建構函式的存在意義--------是通過已有的物件構造新的物件,構造完畢後才有兩個物件;過載賦值操作符的意義-----------將一個物件的值賦給另一個物件,兩個物件都已經構造完畢了。拷貝建構函式----呼叫---

關於拷貝建構函式過載操作符

拷貝建構函式和過載賦值操作符一般都是一起出現的。 拷貝建構函式: A(const A &rhs) { name=rhs.name; age=new int(); *age=*rhs.age; } 過載賦值操作符: A& operator

禁用拷貝建構函式過載運算子

【方法】 1,將複製建構函式和賦值操作符宣告為private。 2,若也不允許友元和成員使用,只提供成員的宣告而不提供定義。這樣當程式中出現 複製或賦值現象時,會造成連結錯誤。 #define DISABLE_COPY(Class) \ Class(const Clas

C++ 複製建構函式運算子過載函式

宣告一個空的類testsize,sizeof(testsize)為1,為其宣告建構函式和解構函式,依舊為1 建構函式不能使用關鍵字virtual,解構函式可以 一旦類中存在虛擬函式,就會為該類生成虛擬函式表,並在每一個例項中新增一個指向虛擬函式表的指標,從而大小為一個指標大

複製建構函式操作符過載

複製建構函式也就拷貝建構函式,只能在物件初始化時呼叫,或在形參賦值時.因為它總是構造一個新物件並將舊物件的屬性值拷貝過去.賦值操作符過載則可以在任何地方呼叫,不管左操作物件為新舊,都是將右操作物件的屬性值拷貝到左操作物件

C++語法,複製建構函式=運算子過載

1、物件在建立時使用其他的物件初始化 Person p(q); //此時複製建構函式被用來建立例項p Person p = q; //此時複製建構函式被用來在定義例項p時初始化p 2、物件作為函式的引數進行值傳遞時 f(p); //此時p作為函式的引數進行值傳遞,p入棧時會呼叫複製建構函式建立一

C++建構函式初始化列表建構函式中的的區別

C++類中成員變數的初始化有兩種方式:          建構函式初始化列表和建構函式體內賦值。下面看看兩種方式有何不同。          成員變數初始化的順序是按照在那種定義的順序。 1、內部資料型別(char,int……指標等) class Animal { publ

C++複製建構函式&移動建構函式複製運算子&移動運算子

一、呼叫時機 1、複製建構函式呼叫的時機 ·物件在建立時使用其他的物件初始化 Person p(q); //此時複製建構函式被用來建立例項p Person p = q; //此時複製建構函式被用來在定義例項p時初始化p return_p()  //當函式返回該型別的物件

C++ 拷貝建構函式函式的區別(很嚴謹和全面)

這裡我們用類String 來介紹這兩個函式: 拷貝建構函式是一種特殊建構函式,具有單個形參,該形參(常用const修飾)是對該類型別的引用。當定義一個新物件並用一個同類型的物件對它進行初始化時,將顯式使用拷貝建構函式。為啥形參必須是對該型別的引用呢?試

c++:類拷貝控制 - 拷貝建構函式 & 拷貝運算子

一、拷貝控制 當定義一個類時,我們可以顯式或隱式地指定此型別的物件拷貝、移動、賦值和銷燬時做什麼。 一個類可以通過定義五種特殊的成員函式來控制這些操作,包括:++拷貝建構函式++、++拷貝賦值函式++、++移動建構函式++、++移動複製函式++和++解構函式++。我們稱這些操作為

C++ 複製建構函式和運算子過載示例

string1.h // // Created by lance on 10/16/18. // #ifndef CPP_PRIMER_STRING1_H #define CPP_PRIMER_STRING1_H #include <iostream> u

C++結構體:預設建構函式複製建構函式過載=運算子

C++結構體提供了比C結構體更多的功能,如預設建構函式,複製建構函式,運算子過載,這些功能使得結構體物件能夠方便的傳值。 比如,我定義一個簡單的結構體,然後將其作為vector元素型別,要使用的話,就需要實現上述三個函式,否則就只能用指標了。 #include

C++11特性--新的類功能--特殊的成員函式(移動建構函式,移動運算子),預設方法和禁用方法(default,delete),委託建構函式,管理虛方法(override,final)

      class A      {         public:            void fun(int x )            {               cout<<x<<endl;             }                      

C++筆記之為什麼一個類定義了解構函式就幾乎肯定要定義拷貝建構函式和拷貝運算子

這個問題本來很簡單,但是時間久了就容易忘,所以做個筆記用來提示下自己 先來看看這樣一個類: class HasPtr { public: HasPtr(const string& s = string()) :ps(new string(s)), i(0) {

C++(建構函式解構函式

C++(建構函式與解構函式) 1. 建構函式 用於對類的物件的初始化,建構函式名與類名相同。 可在類內直接定義,也可在類內宣告類外定義(定義時在函式名前加類名::)。 建構函式無返回值型別。 class C { public: C(int a,int b);//類

預設拷貝建構函式和預設函式

當一個類中有動態分配記憶體時,應當自己定義拷貝建構函式和賦值函式 class A { int *p; public: A() { p = new int[10]; } ~A() {

C++複製建構函式引數問題

今天遇到一個題, #include <iostream> using namespace std; class Sample { public: int v; // 在此處補充你的程式碼 }; void PrintAndDouble(Sample o) { c