1. 程式人生 > >拷貝建構函式的3種呼叫情況分析( C++ )

拷貝建構函式的3種呼叫情況分析( C++ )

拷貝建構函式的3種呼叫情況分析


目錄


什麼是拷貝建構函式?


  • 拷貝建構函式 是一種特殊的建構函式,具有單個形參,該形參( 常用const修飾 )是對該類型別的引用。
  • 如果在類中沒有定義拷貝建構函式,編譯器會自行定義一個。如果類帶有指標變數
    ,並有動態記憶體分配,為了防止淺拷貝帶來的風險,則它必須有一個拷貝建構函式。拷貝建構函式的最常見形式如下:
 classname (const classname &obj) 
 {
   // 建構函式的主體
 }
  • 這裡建構函式傳入的值為什麼需要是引用呢?個人第一反應:為了減少一次記憶體拷貝。其實這樣說是不完整的。眾說周知,函式的值傳遞有隱式地呼叫建構函式以實現臨時引數的初始化,呼叫classname obj = aaa;,又因為obj之前沒有被建立,所以又需要呼叫拷貝建構函式,故而又執行classname obj = aaa,就這樣永遠的遞迴呼叫下去了。所以, 拷貝建構函式是必須要帶引用型別的引數的, 而且這也是編譯器強制性要求的。
  • 這裡為什麼傳引數需宣告為const , 如果在函式中不會改變引用型別引數的值,加不加const的效果是一樣的。而且不加const,編譯器也不會報錯。但是為了整個程式的安全,還是加上const,防止對引用型別引數值的意外修改。

    使用拷貝建構函式的三種情況為:

    • 一個物件需要通過另外一個物件進行初始化
    • 一個物件以值傳遞的方式傳入函式體
    • 一個物件以值傳遞的方式從函式返回

    下面對這三種情況做一下簡介

一個物件需要通過另外一個物件進行初始化


#include <iostream>

using namespace std
; class Line { public: int getLength( void ); Line( int len ); // 簡單的建構函式 Line( const Line &obj); // 拷貝建構函式 ~Line(); // 解構函式 private: int *ptr; }; // 成員函式定義,包括建構函式 Line::Line(int len) { cout << "呼叫建構函式" << endl; // 為指標分配記憶體 ptr = new int; *ptr = len; } Line::Line(const Line &obj) { cout << "呼叫拷貝建構函式併為指標 ptr 分配記憶體" << endl; ptr = new int; *ptr = *obj.ptr; // 拷貝值 } Line::~Line(void) { cout << "釋放記憶體" << endl; delete ptr; } int Line::getLength( void ) { return *ptr; } void display(Line obj) { cout << "line 大小 : " << obj.getLength() <<endl; } // 程式的主函式 int main( ) { Line line1(10);//這裡會呼叫一次建構函式 Line line2 = line1; // 這裡也呼叫了拷貝建構函式 display(line1); display(line2); return 0; }

呼叫建構函式
呼叫拷貝建構函式併為指標 ptr 分配記憶體
呼叫拷貝建構函式併為指標 ptr 分配記憶體
line 大小 : 10
釋放記憶體
呼叫拷貝建構函式併為指標 ptr 分配記憶體
line 大小 : 10
釋放記憶體
釋放記憶體
釋放記憶體

一個物件以值傳遞的方式傳入函式體


#include <iostream>

using namespace std;

class Line
{
   public:
      int getLength( void );
      Line( int len );             // 簡單的建構函式
      Line( const Line &obj);      // 拷貝建構函式
      ~Line();                     // 解構函式

   private:
      int *ptr;
};

// 成員函式定義,包括建構函式
Line::Line(int len)
{
    cout << "呼叫建構函式" << endl;
    // 為指標分配記憶體
    ptr = new int;
    *ptr = len;
}

Line::Line(const Line &obj)
{
    cout << "呼叫拷貝建構函式併為指標 ptr 分配記憶體" << endl;
    ptr = new int;
    *ptr = *obj.ptr; // 拷貝值
}

Line::~Line(void)
{
    cout << "釋放記憶體" << endl;
    delete ptr;
}
int Line::getLength( void )
{
    return *ptr;
}

void display(Line obj/*這裡會呼叫一次拷貝建構函式*/)
{
   cout << "line 大小 : " << obj.getLength() <<endl;
}

// 程式的主函式
int main( )
{
   Line line(10);//這裡會呼叫一次建構函式

   display(line);

   return 0;
}

呼叫建構函式
呼叫拷貝建構函式併為指標 ptr 分配記憶體
line 大小 : 10
釋放記憶體
釋放記憶體

一個物件以值傳遞的方式從函式返回


#include <iostream>

using namespace std;

class Line
{
public:
    int getLength(void);
    Line(int len);             // 簡單的建構函式
    Line(const Line &obj);      // 拷貝建構函式
    Line returnLine(int i);

    ~Line();                     // 解構函式

private:
    int *ptr;
};

// 成員函式定義,包括建構函式
Line::Line(int len)
{
    cout << "呼叫建構函式" << endl;
    // 為指標分配記憶體
    ptr = new int;
    *ptr = len;
}

Line::Line(const Line &obj)
{
    cout << "呼叫拷貝建構函式併為指標 ptr 分配記憶體" << endl;
    ptr = new int;
    *ptr = *obj.ptr; // 拷貝值
}

Line::~Line(void)
{
    cout << "釋放記憶體" << endl;
    delete ptr;
}

Line Line::returnLine(int i)
{
    Line line(1);//這裡會呼叫一次建構函式
    return line;//這裡會呼叫一次拷貝建構函式
}
int Line::getLength(void)
{
    return *ptr;
}



// 程式的主函式
int main()
{
    Line line1(10);
    line1.returnLine(2);

    system("pause");
    return 0;
}

結果

呼叫建構函式
呼叫建構函式
呼叫拷貝建構函式併為指標 ptr 分配記憶體
釋放記憶體
釋放記憶體