C++动态数组类的封装实例(2)
当我们定义重载的运算符时,必须首先决定是将其声明为类的成员函数还是声明为一个普通的非成员函数。有些运算符必须作为成员,而另一些运算符作为普通函数比作为成员更好:
赋值(=)、下标([ ])、调用(( ))和成员访问箭头(->)运算符必须是成员。
复合赋值运算符一般来说应该是成员,但并非必须,这一点与赋值运算符略有不同。
改变对象状态的运算符或者与给定类型密切相关的运算符,如递增、递减、解引用运算符,通常应该是成员。
具有对称性的运算符可能转换任意一端的运算对象,例如算术、相等性、关系和位运算符等,因此它们通常应该是普通的非成员函数。
当然,除了赋值运算符之外,我们还需要为动态数组定义下标运算符operator []。下标运算符必须是成员函数。为了让下标可以出现在赋值运算符的任意一端,下标运算符函数通常返回所访问元素的引用。
四、动态数组类的封装
下面给出了动态数组DArray类的接口:
class DArray { private: double *m_Data; // 存放数组的动态内存指针 int m_Size; // 数组的元素个数 int m_Max; // 预留给动态数组的内存大小 private: void Init(); // 初始化 void Free(); // 释放动态内存 inline bool InvalidateIndex(int nIndex); // 判断下标的合法性 public: DArray(); // 默认构造函数 DArray(int nSize, double dValue = 0); // 构造函数,设置数组大小,默认值为dValue DArray(const DArray& arr); // 拷贝构造函数 DArray& operator=(const DArray& arr); // 拷贝赋值运算符 ~DArray(); // 析构函数 void Print(); // 输出显式所有数组元素的值 int GetSize(); // 获取数组的大小(元素个数) void SetSize(int nSize); // 重新设置数组的大小,若nSize小于原大小,截断;否则,新元素置0 double GetAt(int nIndex); // 获取指定位置元素 void SetAt(int nIndex,double dValue); // 重置指定元素的值 void PushBack(double dValue); // 追加一个新元素到数组末尾 void DeleteAt(int nIndex); // 删除指定位置地元素 void InsertAt(int nIndex, double dValue); // 插入一个新的元素到数组中 double operator[](int nIndex) const; // 重载下标运算符[] };
下面是实现方法:
void DArray::Init() { m_Size = 0; // 默认情况下数组不包含元素 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; } // 默认构造函数 DArray::DArray() { Init(); } // 构造函数 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; } } // 拷贝构造函数 DArray::DArray(const DArray& arr) { m_Size = arr.m_Size; /*复制常规成员*/ 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; } // 析构函数 DArray::~DArray() { Free(); } // 打印数组 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; } } // 获取数组大小 int DArray::GetSize() { return m_Size; } // 重置数组大小 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; /*设置数组大小*/ } // 获取指定位置元素 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; } } // 追加一个新元素到数组末尾 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; /*数组大小加1*/ } // 从数组中删除一个元素 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; /* 数组大小加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]; }
经过简单的测试,暂时还没有发现Bug。可能测试并不全面,感兴趣的读者可以进一步测试并完善该程序。
附:String类的实现
C++ 的一个常见面试题是让你实现一个 String 类,限于时间,不可能要求具备 std::string 的功能,但至少要求能正确管理资源。