最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

C++動態(tài)數(shù)組類的封裝實例

 更新時間:2014年08月15日 15:35:03   投稿:shichen2014  
這篇文章主要介紹了C++動態(tài)數(shù)組類的封裝,很重要的概念,需要的朋友可以參考下

C++中的動態(tài)數(shù)組(Dynamic Array)是指動態(tài)分配的、可以根據(jù)需求動態(tài)增長占用內存的數(shù)組。為了實現(xiàn)一個動態(tài)數(shù)組類的封裝,我們需要考慮幾個問題:new/delete的使用、內存分配策略、類的四大函數(shù)(構造函數(shù)、拷貝構造函數(shù)、拷貝賦值運算符、析構函數(shù))、運算符的重載。涉及到的知識點很多,對此本文只做簡單的介紹。

一、內存分配策略

當用new為一個動態(tài)數(shù)組申請一塊內存時,數(shù)組中的元素是連續(xù)存儲的,例如 vector和string。當向一個動態(tài)數(shù)組添加元素時,如果沒有空間容納新元素,不可能簡單地將新元素添加到內存中的其他位置——因為元素必須連續(xù)存儲。所以必須重新分配一塊更大的內存空間,將原來的元素從舊位置移動到新空間中,然后添加新元素,釋放舊的內存空間。如果我們每添加一個新元素,就執(zhí)行一次這樣的內存分配和釋放操作,效率將會慢到不行。

為了避免上述的代價,必須減少內存重新分配的次數(shù)。所以我們采取的策略是:在不得不分配新的內存空間時,分配比新的空間需求更大的內存空間(通常為2倍)。這樣,在相當一段時間內,添加元素時就不用重新申請內存空間。注意,只有當迫不得已時才可以分配新的內存空間。

二、類的四大函數(shù)

一個C++類一般至少有四大函數(shù),即構造函數(shù)、拷貝構造函數(shù)、拷貝賦值運算符、析構函數(shù)。如果類未自己定義上述函數(shù),C++編譯器將為其合成4個默認的版本。但是往往編譯器合成的并不是我們所期望的,為此我們有必要自己定義它們。

1.構造函數(shù)

類的構造函數(shù)(constructor)用來初始化類對象的非static數(shù)據(jù)成員,無論何時只要類的對象被創(chuàng)建,就會執(zhí)行構造函數(shù)。

class Foo { 
public: 
  Foo(); // 構造函數(shù) 
  Foo(string &s); 
  // ... 
}; 

構造函數(shù)的名字和類名相同,沒有返回類型。類可以包含多個構造函數(shù)(重載),它們之間在參數(shù)數(shù)量或類型上需要有所區(qū)別。構造函數(shù)有一個初始化部分和一個函數(shù)體,成員的初始化是在函數(shù)體執(zhí)行之前完成的。

2.拷貝構造函數(shù)

如果一個構造函數(shù)的第一個參數(shù)是自身類類型的引用,且任何額外參數(shù)都有默認值,則此構造函數(shù)是拷貝構造函數(shù)(copy constructor)。

class Foo { 
public: 
  Foo(); 
  Foo(const Foo&); // 拷貝構造函數(shù) 
  // ... 
}; 

拷貝構造函數(shù)定義了如何用一個對象初始化另一個同類型的對象。拷貝初始化通常使用拷貝構造函數(shù)來完成。拷貝初始化發(fā)生在下列情況中:

使用等號(=)初始化一個變量
將一個對象作為實參傳遞給一個非引用類型的形參
從一個返回類型為非引用類型的函數(shù)返回一個對象
用花括號列表初始化一個數(shù)組中的元素

3.拷貝賦值運算符

類的拷貝賦值運算符(copy-assignment operator)是一個名為operator=的函數(shù)。類似于其他任何函數(shù),它也有一個返回類型和一個參數(shù)列表。

class Foo { 
public: 
  Foo(); 
  Foo& operator=(const Foo&); // 賦值運算符 
  // ... 
}; 

拷貝賦值運算符定義了如何將一個對象賦值給另一個同類型的對象。賦值運算符是一個成員函數(shù)也是一個二元運算符,其左側運算對象就綁定到隱式的this指針,右側運算對象作為顯式參數(shù)傳遞。注意:為了與內置類型的賦值保持一致,賦值運算符通常返回一個指向其左側運算對象的引用。

4.析構函數(shù)

類的析構函數(shù)(destructor)用來釋放類對象使用的資源并銷毀類對象的非static數(shù)據(jù)成員,無論何時只要一個對象被銷毀,就會自動執(zhí)行析構函數(shù)。

class Foo { 
public: 
  ~Foo(); // 析構函數(shù) 
  // ... 
}; 

析構函數(shù)的名字由波浪號(~)加類名構成,也沒有返回類型。由于析構函數(shù)不接受參數(shù),因此它不能被重載。析構函數(shù)有一個函數(shù)體和一個析構部分,銷毀一個對象時,首先執(zhí)行析構函數(shù)體,然后按初始化順序的逆序銷毀成員。

三、運算符的重載

重載的運算符是具有特殊名字的函數(shù):它們的名字由關鍵字operator和其后要定義的運算符號共同組成。和其他函數(shù)一樣,重載的運算符也包含返回類型、參數(shù)列表、函數(shù)體,比如拷貝賦值運算符。

當我們定義重載的運算符時,必須首先決定是將其聲明為類的成員函數(shù)還是聲明為一個普通的非成員函數(shù)。有些運算符必須作為成員,而另一些運算符作為普通函數(shù)比作為成員更好:

賦值(=)、下標([ ])、調用(( ))和成員訪問箭頭(->)運算符必須是成員。
復合賦值運算符一般來說應該是成員,但并非必須,這一點與賦值運算符略有不同。
改變對象狀態(tài)的運算符或者與給定類型密切相關的運算符,如遞增、遞減、解引用運算符,通常應該是成員。
具有對稱性的運算符可能轉換任意一端的運算對象,例如算術、相等性、關系和位運算符等,因此它們通常應該是普通的非成員函數(shù)。
當然,除了賦值運算符之外,我們還需要為動態(tài)數(shù)組定義下標運算符operator []。下標運算符必須是成員函數(shù)。為了讓下標可以出現(xiàn)在賦值運算符的任意一端,下標運算符函數(shù)通常返回所訪問元素的引用。

四、動態(tài)數(shù)組類的封裝

下面給出了動態(tài)數(shù)組DArray類的接口:

class DArray 
{ 
private: 
  double *m_Data; // 存放數(shù)組的動態(tài)內存指針 
  int m_Size;   // 數(shù)組的元素個數(shù) 
  int m_Max;    // 預留給動態(tài)數(shù)組的內存大小 
private: 
  void Init();   // 初始化 
  void Free();   // 釋放動態(tài)內存 
  inline bool InvalidateIndex(int nIndex); // 判斷下標的合法性 
public: 
  DArray();    // 默認構造函數(shù) 
  DArray(int nSize, double dValue = 0); // 構造函數(shù),設置數(shù)組大小,默認值為dValue 
  DArray(const DArray& arr); // 拷貝構造函數(shù) 
  DArray& operator=(const DArray& arr); // 拷貝賦值運算符 
  ~DArray();    // 析構函數(shù) 
 
  void Print();  // 輸出顯式所有數(shù)組元素的值 
  int GetSize();  // 獲取數(shù)組的大?。ㄔ貍€數(shù)) 
  void SetSize(int nSize); // 重新設置數(shù)組的大小,若nSize小于原大小,截斷;否則,新元素置0 
  double GetAt(int nIndex); // 獲取指定位置元素 
  void SetAt(int nIndex,double dValue); // 重置指定元素的值 
  void PushBack(double dValue); // 追加一個新元素到數(shù)組末尾 
  void DeleteAt(int nIndex);   // 刪除指定位置地元素 
  void InsertAt(int nIndex, double dValue); // 插入一個新的元素到數(shù)組中 
  double operator[](int nIndex) const;   // 重載下標運算符[] 
}; 

下面是實現(xiàn)方法:

void DArray::Init() 
{ 
  m_Size = 0;  // 默認情況下數(shù)組不包含元素 
  m_Max = 1; 
  m_Data = new double[m_Max]; 
} 
 
void DArray::Free() 
{ 
  delete [] m_Data; 
} 
 
bool DArray::InvalidateIndex(int nIndex) 
{ 
  if(nIndex>=0 && nIndex<m_Size) 
    return false; 
  else 
    return true; 
} 
 
// 默認構造函數(shù) 
DArray::DArray() 
{ 
  Init(); 
} 
 
// 構造函數(shù) 
DArray::DArray(int nSize, double dValue) 
{ 
  if(nSize == 0) 
    Init(); 
  else 
  { 
    m_Size = nSize; 
    m_Max = nSize; 
    m_Data = new double[m_Max]; 
    for(int i=0; i<nSize; ++i) 
      m_Data[i]=dValue; 
  } 
} 
 
// 拷貝構造函數(shù) 
DArray::DArray(const DArray& arr) 
{ 
  m_Size = arr.m_Size; /*復制常規(guī)成員*/ 
  m_Max = arr.m_Max; 
  m_Data = new double[m_Max];  /*復制指針指向的內容*/ 
  memcpy(m_Data, arr.m_Data, m_Size*sizeof(double)); 
} 
 
// 拷貝賦值運算符 
DArray& DArray::operator=(const DArray& arr) 
{ 
  if(this == &arr) /*自賦值*/ 
    return *this; 
  m_Size = arr.m_Size; 
  m_Max = arr.m_Max; 
  /* 先將右側對象拷貝到臨時對象中,然后再銷毀左側對象*/  
  double *m_Temp = new double[m_Max]; 
  memcpy(m_Temp, arr.m_Data, m_Size*sizeof(double)); 
  delete [] m_Data; 
  m_Data = m_Temp; 
 
  return *this; 
} 
 
// 析構函數(shù) 
DArray::~DArray() 
{ 
  Free(); 
} 
 
// 打印數(shù)組 
void DArray::Print() 
{ 
  if(m_Size == 0) 
  { 
    cout << "Error: The empty array can't be Printed." << endl; 
    exit(0); 
  } 
  else 
  { 
    for(int i=0; i<m_Size; ++i) 
      cout << m_Data[i] << " "; 
    cout << endl; 
  } 
} 
 
// 獲取數(shù)組大小 
int DArray::GetSize() 
{ 
  return m_Size; 
} 
 
// 重置數(shù)組大小 
void DArray::SetSize(int nSize) 
{ 
  if(nSize < m_Size)  /*截斷*/ 
  { 
    for(int i=nSize; i<m_Size; ++i) 
      m_Data[i] = 0; 
  } 
  if(m_Size<=nSize && nSize<=m_Max) /*新增元素置0*/ 
  { 
    for(int i=m_Size; i<nSize; ++i) 
      m_Data[i] = 0; 
  } 
  if(nSize > m_Max)  /*需要重新分配空間*/ 
  { 
    m_Max = nSize; 
    double *temp = new double[m_Max]; 
    memcpy(temp, m_Data, m_Size*sizeof(double)); 
    for(int i=m_Size; i<nSize; ++i) 
      temp[i] = 0; 
    delete [] m_Data; 
    m_Data = temp; 
  } 
  m_Size = nSize; /*設置數(shù)組大小*/ 
} 
 
// 獲取指定位置元素 
double DArray::GetAt(int nIndex) 
{ 
  if(InvalidateIndex(nIndex)) 
  { 
    cout << "Error: the index of GetAt is invalid!" << endl; 
    exit(0); 
  } 
  return m_Data[nIndex]; 
} 
 
// 設置指定位置元素的值 
void DArray::SetAt(int nIndex, double dValue) 
{ 
  if(InvalidateIndex(nIndex)) 
  { 
    cout << "Error: the index of SetAt is invalid!" << endl; 
    exit(0); 
  } 
  else 
  { 
    m_Data[nIndex] = dValue; 
  } 
} 
 
// 追加一個新元素到數(shù)組末尾 
void DArray::PushBack(double dValue) 
{ 
  if(m_Size < m_Max) 
  { 
    m_Data[m_Size] = dValue; 
  } 
  else  
  { 
    m_Max = m_Max*2; 
    double* temp = new double[m_Max]; 
    memcpy(temp, m_Data, m_Size*sizeof(double)); 
    delete [] m_Data; 
    m_Data = temp; 
    m_Data[m_Size] = dValue; 
  } 
  ++m_Size; /*數(shù)組大小加1*/ 
} 
 
// 從數(shù)組中刪除一個元素 
void DArray::DeleteAt(int nIndex) 
{ 
  if(InvalidateIndex(nIndex)) 
  { 
    cout << "Error: the index of DeleteAt is invalid." << endl; 
    exit(0); 
  } 
  else 
  {   
    for(int i=nIndex; i<m_Size; ++i) 
      m_Data[i] = m_Data[i+1]; 
    m_Data[m_Size-1] = 0; 
    --m_Size; 
  } 
} 
 
// 插入一個新元素到指定位置 
void DArray::InsertAt(int nIndex, double dValue) 
{ 
  if(nIndex<0 || nIndex>m_Size) 
  { 
    cout << "Error: the index of InsertAt is invalid!" << endl; 
    exit(0); 
  } 
 
  if(m_Size < m_Max) /* 未滿,插入 */ 
  { 
    for(int i=m_Size-1; i>=nIndex; --i) 
      m_Data[i+1] = m_Data[i]; 
    m_Data[nIndex] = dValue; 
  } 
  else        /* 重新分配空間 */ 
  { 
    m_Max = m_Max*2; 
    double* temp = new double[m_Max]; 
    memcpy(temp, m_Data, m_Size*sizeof(double)); 
    delete [] m_Data; 
    m_Data = temp; 
    for(int i=m_Size-1; i>=nIndex; --i) 
      m_Data[i+1] = m_Data[i]; 
    m_Data[nIndex] = dValue; 
  } 
  ++m_Size; /* 數(shù)組大小加1 */ 
} 
 
// 重載下標運算符[] 
double DArray::operator[](int nIndex) const 
{ 
  if(nIndex<0 || nIndex>=m_Size) 
  { 
    cout << "Error: the index in [] is invalid!" << endl; 
    exit(0); 
  } 
  return m_Data[nIndex]; 
} 

經(jīng)過簡單的測試,暫時還沒有發(fā)現(xiàn)Bug??赡軠y試并不全面,感興趣的讀者可以進一步測試并完善該程序。

附:String類的實現(xiàn)

C++ 的一個常見面試題是讓你實現(xiàn)一個 String 類,限于時間,不可能要求具備 std::string 的功能,但至少要求能正確管理資源。

如果你弄懂了上面DArray類的寫法,那么實現(xiàn)String類應該就不難了。因為面試官一般只是想考查你能不能正確地寫出構造函數(shù)、析構函數(shù)、拷貝構造函數(shù)、拷貝賦值運算符以及+、[ ]、<<、>>運算符重載等等。下面給出一個String類的接口,你可以自己試試手實現(xiàn)一下:

class String{  
  friend ostream& operator<< (ostream&,String&); //重載<<運算符  
  friend istream& operator>> (istream&,String&); //重載>>運算符  
public:  
  String();  // 默認構造函數(shù) 
  String(const char* str);    // 帶參構造函數(shù)  
  String(const String& rhs);    // 拷貝構造函數(shù)  
  String& operator=(const String& rhs);  // 拷貝賦值運算符  
  String operator+(const String& rhs) const; //operator+  
  bool operator==(const String&);       //operator== 
  bool operator!=(const String&);       //operator!=   
  char& operator[](unsigned int);       //operator[]  
  size_t size() const;  
  const char* c_str() const; 
  ~String();  // 析構函數(shù)  
private:  
  char *m_data; // 用于保存字符串  
}; 

本文所述DArray類和String類的源碼及測試代碼可點擊此處本站下載。

相關文章

  • C語言學生成績管理系統(tǒng)設計

    C語言學生成績管理系統(tǒng)設計

    這篇文章主要為大家詳細介紹了C語言學生成績管理系統(tǒng)設計,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-01-01
  • c++?qt自定義搜索編輯框的實現(xiàn)方法

    c++?qt自定義搜索編輯框的實現(xiàn)方法

    這篇文章主要介紹了c++?qt自定義搜索編輯框,通過自定義QLineEdit,在編輯框里添加布局,將按鈕設置在右邊,當點擊按鈕搜索按鈕時發(fā)送信號到主界面做相應的操作,需要的朋友可以參考下
    2022-03-03
  • C++中rapidjson將嵌套map轉為嵌套json的講解

    C++中rapidjson將嵌套map轉為嵌套json的講解

    今天小編就為大家分享一篇關于C++中rapidjson將嵌套map轉為嵌套json的講解,小編覺得內容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2019-04-04
  • c語言和c++語言中const修飾的變量區(qū)別淺析

    c語言和c++語言中const修飾的變量區(qū)別淺析

    這篇文章主要給大家介紹了關于c語言和c++語言中const修飾的變量區(qū)別的相關資料,文中通過實例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2022-02-02
  • Visual Studio新建類從默認internal改為public

    Visual Studio新建類從默認internal改為public

    本文將介紹如何將Visual Studio中的internal修飾符更改為public,以實現(xiàn)更廣泛的訪問和重用,需要的朋友們下面隨著小編來一起學習學習吧
    2023-09-09
  • VSCode下.json文件的編寫之(1) linux/g++ (2).json中參數(shù)與預定義變量的意義解釋

    VSCode下.json文件的編寫之(1) linux/g++ (2).json中參數(shù)與預定義變量的意義解釋

    這篇文章主要介紹了VSCode下.json文件的編寫之(1) linux/g++ (2).json中參數(shù)與預定義變量的意義解釋,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-03-03
  • C++ LeetCode543題解二叉樹直徑

    C++ LeetCode543題解二叉樹直徑

    這篇文章主要為大家介紹了C++ LeetCode543題解二叉樹直徑,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-12-12
  • 淺談C語言數(shù)組元素下標為何從0開始

    淺談C語言數(shù)組元素下標為何從0開始

    很多同學可能在學習數(shù)組時會有這個疑問,下標為什么不從1開始呢?本文主要介紹了淺談C語言數(shù)組元素下標為何從0開始,感興趣的可以了解一下
    2022-01-01
  • C++ deque容器的用法詳解

    C++ deque容器的用法詳解

    在處理一些數(shù)組的事情,所以隨手保留一下Deque容器的使用方法很有必要,接下來通過本文給大家重點介紹C++ deque容器的用法及deque和vector的區(qū)別講解,感興趣的朋友跟隨小編一起看看吧
    2021-05-05
  • VS2019安裝配置MFC(安裝vs2019時沒有安裝mfc)

    VS2019安裝配置MFC(安裝vs2019時沒有安裝mfc)

    這篇文章主要介紹了VS2019安裝配置MFC(安裝vs2019時沒有安裝mfc),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-03-03

最新評論

鹤峰县| 平罗县| 吴江市| 贵定县| 郴州市| 永靖县| 岐山县| 晋江市| 巴楚县| 北宁市| 奉化市| 兰考县| 宁城县| 宁津县| 鄂州市| 什邡市| 寻甸| 丹江口市| 周口市| 屯昌县| 丹巴县| 广宁县| 安宁市| 寻乌县| 靖宇县| 辽阳县| 彭州市| 偏关县| 墨玉县| 策勒县| 富源县| 鄂尔多斯市| 定兴县| 靖西县| 霍山县| 晋城| 凭祥市| 呼图壁县| 监利县| 泸溪县| 永吉县|