C++ 手把手教你實現可變長的數組

  • 2020 年 3 月 31 日
  • 筆記

1

要實現什麼函數呢?

假設我們要實現一個會自動擴展的數組類,我們需要實現函數呢?先從下面 main 函數使用的功能,看看有什麼函數是需要我們實現的。

輸出結果:

0 1 2 3 4  0 1 2 100 4

要實現如上的功能,要做哪些事情呢?先列出來:

  • 要用動態分配的記憶體的方式,來存放數組元素,且需要一個指針成員變數
  • 重載賦值 = 運算符
  • 重載括弧 [] 運算符
  • 重載複製構造函數
  • 實現 push_back 和 length 函數

2

實現的步驟

要實現一個可變長的數組類,基本要需要實現下面的 7 個函數:

— —

01 構造函數

構造函數的目的就是初始化一個數組,程式碼如下:

// 構造函數  MyArray::MyArray(int s = 0):m_size(s)  {      // 當初始化長度為0的數組時,數組指針就是空的      if(s == 0)          m_ptr = NULL;      // 當初始化長度不為0時,則申請對應大小的空間      else          m_ptr = new int[s];  }

— —

02 複製構造函數

複製構造函數目的就是產生一個與入參對象一樣的對象,但是由於 MyArray 類是有指針成員變數的,所以我們必須用深拷貝的方式來實現複製構造函數,如果使用默認的複製構造函數,則會導致兩個對象的指針成員變數指向的地址是同一個,這是非常危險的。

// 複製構造函數  MyArray::MyArray(const MyArray &a)  {      // 如果入參的數組對象的指針地址為空時,      // 則也初始化一個空的數組      if(a.m_ptr == NULL)      {          m_ptr = NULL;          m_size = 0;      }      // 如果入參的數組對象有數據時,則申請一個新的地址,      // 最後來複制入參對象數組對象的數據和大小。      else      {          m_ptr = new int[a.m_size];          memcpy(m_ptr, a.m_ptr, sizeof(int)*a.m_size);          m_size = a.m_size;      }  }

— —

03 析構函數

析構函數的目的就是釋放數組的資源,程式碼如下:

// 析構函數  MyArray::~MyArray()  {      // 如果指針地址不為空時,則釋放資源      if(m_ptr)          delete [] m_ptr;  }

— —

04 重載賦值 = 運算符函數

重載賦值 = 運算符函數目的就是 = 號左邊對象里存放的數組,大小和內容都和右邊的對象一樣,程式碼如下:

// 重載賦值 = 運算符函數  MyArray & MyArray::operator=(const MyArray & a)  {      if(m_ptr == a.m_ptr) // 防止a=a這樣的賦值導致出錯          return *this;        if(a.m_ptr == NULL) // 如果a裡面的數組是空的      {          if(m_ptr)              delete [] m_ptr; // 釋放舊數組的資源            m_ptr = NULL;          m_size = 0;          return *this;      }        // 如果原有空間足夠大,就不用分配新的空間      if(m_size < a.m_size)用分配新的空間      {          if(m_ptr)              delete [] m_ptr; // 釋放舊數組的資源            m_ptr = new int[a.m_size]; // 申請新的記憶體地址      }        // 拷貝內容      memcpy(m_ptr, a.m_ptr, sizeof(int)*a.m_size);      m_size = a.m_size;      return *this;  }

— —

05 重載 [] 運算符函數

重載 [] 運算符函數目的就是能通過 [] 運算符來獲取對應下標的數組值,程式碼如下:

// 重載[]運算符函數  int & MyArray::operator[](int i)  {      return m_ptr[i]; // 返回對應下標的數組值  }

— —

06 加入元素到數組末尾的函數

push_back 函數的目的就是把一個新的元素,加入到數組的末尾,程式碼如下:

// 在數組尾部添加一個元素  void MyArray::push_back(int v)  {      if(m_ptr) // 如果數組不為空      {          // 重新分配空間          int *tmpPtr = new int[m_size + 1];            // 拷貝原數組內容          memcpy(tmpPtr, m_ptr, sizeof(int)*m_size);            delect [] m_ptr;          m_ptr = tmpPtr;      }      else // 如果數組本來就是空的      {          m_ptr = new int[1];      }        m_ptr[m_size++] = v; //加入新的數組元素  }

— —

07 獲取數組長度的函數

length 函數就比較簡單了,直接返回成員變數 m_size,就是數組的長度了,程式碼如下:

// 獲取數組長度的函數  int MyArray:;length()  {      return m_size;  }

04

再改進下?

push_back 函數還有優化的空間,當前的 push_back 函數每加入一個元素都會重新分配新的記憶體,這是會增大開銷的。

那麼優化的思路: 提前分配好一個 n 大小的空間,當數組大小不夠的時候,則才繼續重新分配 2n 大小的空間,以此類推。

我們需要新增兩個成員變數:

 int  m_cout;   // 數組元素的個數   int  m_newNum; // 擴容的次數

改進後的 push_back 函數,程式碼如下:


5

小結

最後,總體的程式碼如下: