• linkedu视频
  • 平面设计
  • 电脑入门
  • 操作系统
  • 办公应用
  • 电脑硬件
  • 动画设计
  • 3D设计
  • 网页设计
  • CAD设计
  • 影音处理
  • 数据库
  • 程序设计
  • 认证考试
  • 信息管理
  • 信息安全
菜单
linkedu.com
  • 网页制作
  • 数据库
  • 程序设计
  • 操作系统
  • CMS教程
  • 游戏攻略
  • 脚本语言
  • 平面设计
  • 软件教程
  • 网络安全
  • 电脑知识
  • 服务器
  • 视频教程
  • JavaScript
  • ASP.NET
  • PHP
  • 正则表达式
  • AJAX
  • JSP
  • ASP
  • Flex
  • XML
  • 编程技巧
  • Android
  • swift
  • C#教程
  • vb
  • vb.net
  • C语言
  • Java
  • Delphi
  • 易语言
  • vc/mfc
  • 嵌入式开发
  • 游戏开发
  • ios
  • 编程问答
  • 汇编语言
  • 微信小程序
  • 数据结构
  • OpenGL
  • 架构设计
  • qt
  • 微信公众号
您的位置:首页 > 程序设计 >C语言 > C++动态数组类的封装实例

C++动态数组类的封装实例

作者: 字体:[增加 减小] 来源:互联网 时间:2017-05-28

通过本文主要向大家介绍了c++动态二维数组,c++动态数组,c++动态数组定义,c++动态分配二维数组,c++动态分配数组等相关知识,希望对您有所帮助,也希望大家支持linkedu.com www.linkedu.com

C++中的动态数组(Dynamic Array)是指动态分配的、可以根据需求动态增长占用内存的数组。为了实现一个动态数组类的封装,我们需要考虑几个问题:new/delete的使用、内存分配策略、类的四大函数(构造函数、拷贝构造函数、拷贝赋值运算符、析构函数)、运算符的重载。涉及到的知识点很多,对此本文只做简单的介绍。

一、内存分配策略

当用new为一个动态数组申请一块内存时,数组中的元素是连续存储的,例如 vector和string。当向一个动态数组添加元素时,如果没有空间容纳新元素,不可能简单地将新元素添加到内存中的其他位置——因为元素必须连续存储。所以必须重新分配一块更大的内存空间,将原来的元素从旧位置移动到新空间中,然后添加新元素,释放旧的内存空间。如果我们每添加一个新元素,就执行一次这样的内存分配和释放操作,效率将会慢到不行。

为了避免上述的代价,必须减少内存重新分配的次数。所以我们采取的策略是:在不得不分配新的内存空间时,分配比新的空间需求更大的内存空间(通常为2倍)。这样,在相当一段时间内,添加元素时就不用重新申请内存空间。注意,只有当迫不得已时才可以分配新的内存空间。

二、类的四大函数

一个C++类一般至少有四大函数,即构造函数、拷贝构造函数、拷贝赋值运算符、析构函数。如果类未自己定义上述函数,C++编译器将为其合成4个默认的版本。但是往往编译器合成的并不是我们所期望的,为此我们有必要自己定义它们。

1.构造函数

类的构造函数(constructor)用来初始化类对象的非static数据成员,无论何时只要类的对象被创建,就会执行构造函数。

class Foo { 
public: 
  Foo(); // 构造函数 
  Foo(string &s); 
  // ... 
}; 
</div>

构造函数的名字和类名相同,没有返回类型。类可以包含多个构造函数(重载),它们之间在参数数量或类型上需要有所区别。构造函数有一个初始化部分和一个函数体,成员的初始化是在函数体执行之前完成的。

2.拷贝构造函数

如果一个构造函数的第一个参数是自身类类型的引用,且任何额外参数都有默认值,则此构造函数是拷贝构造函数(copy constructor)。

class Foo { 
public: 
  Foo(); 
  Foo(const Foo&); // 拷贝构造函数 
  // ... 
}; 
</div>

拷贝构造函数定义了如何用一个对象初始化另一个同类型的对象。拷贝初始化通常使用拷贝构造函数来完成。拷贝初始化发生在下列情况中:

使用等号(=)初始化一个变量
将一个对象作为实参传递给一个非引用类型的形参
从一个返回类型为非引用类型的函数返回一个对象
用花括号列表初始化一个数组中的元素

3.拷贝赋值运算符

类的拷贝赋值运算符(copy-assignment operator)是一个名为operator=的函数。类似于其他任何函数,它也有一个返回类型和一个参数列表。

class Foo { 
public: 
  Foo(); 
  Foo& operator=(const Foo&); // 赋值运算符 
  // ... 
}; 
</div>

拷贝赋值运算符定义了如何将一个对象赋值给另一个同类型的对象。赋值运算符是一个成员函数也是一个二元运算符,其左侧运算对象就绑定到隐式的this指针,右侧运算对象作为显式参数传递。注意:为了与内置类型的赋值保持一致,赋值运算符通常返回一个指向其左侧运算对象的引用。

4.析构函数

类的析构函数(destructor)用来释放类对象使用的资源并销毁类对象的非static数据成员,无论何时只要一个对象被销毁,就会自动执行析构函数。

class Foo { 
public: 
  ~Foo(); // 析构函数 
  // ... 
}; 
</div>

析构函数的名字由波浪号(~)加类名构成,也没有返回类型。由于析构函数不接受参数,因此它不能被重载。析构函数有一个函数体和一个析构部分,销毁一个对象时,首先执行析构函数体,然后按初始化顺序的逆序销毁成员。

三、运算符的重载

重载的运算符是具有特殊名字的函数:它们的名字由关键字operator和其后要定义的运算符号共同组成。和其他函数一样,重载的运算符也包含返回类型、参数列表、函数体,比如拷贝赋值运算符。

当我们定义重载的运算符时,必须首先决定是将其声明为类的成员函数还是声明为一个普通的非成员函数。有些运算符必须作为成员,而另一些运算符作为普通函数比作为成员更好:

赋值(=)、下标([ ])、调用(( ))和成员访问箭头(->)运算符必须是成员。
复合赋值运算符一般来说应该是成员,但并非必须,这一点与赋值运算符略有不同。
改变对象状态的运算符或者与给定类型密切相关的运算符,如递增、递减、解引用运算符,通常应该是成员。
具有对称性的运算符可能转换任意一端的运算对象,例如算术、相等性、关系和位运算符等,因此它们通常应该是普通的非成员函数。
当然,除了赋值运算符之外,我们还需要为动态数组定义下标运算符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;   // 重载下标运算符[] 
}; 

</div>

下面是实现方法:

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;



 
分享到:QQ空间新浪微博腾讯微博微信百度贴吧QQ好友复制网址打印

您可能想查找下面的文章:

  • C++二维数组中的查找算法示例
  • C++中求旋转数组中的最小数字(经典面试题)
  • C与C++动态分配二维数组的实现方法
  • C++实现将数组中的值反转
  • C++实现当前时间动态显示的方法
  • C++ 动态创建按钮及 按钮的消息响应
  • C++动态规划之最长公子序列实例
  • C++动态规划之背包问题解决方法
  • C++实现旋转数组的二分查找
  • C++对数组的引用实例分析

相关文章

  • 2017-05-28深入理解结构体中占位符的用法
  • 2017-05-28简要解读C++的动态和静态关联以及虚析构函数
  • 2017-05-28如何用C语言画一个“圣诞树”
  • 2017-05-28VC++获得当前进程运行目录的方法
  • 2017-05-28c语言中用字符串数组显示菜单的解决方法
  • 2017-05-28详解C++编程中的单目运算符重载与双目运算符重载
  • 2017-05-28浅析C语言编程中的数组越界问题
  • 2017-05-28C语言 数据结构中栈的实现代码
  • 2017-05-28C语言中实现“17进制”转“10进制”实例代码
  • 2017-05-28新旧MFC版本实现CEdit透明的2种方法的实例代码

文章分类

  • JavaScript
  • ASP.NET
  • PHP
  • 正则表达式
  • AJAX
  • JSP
  • ASP
  • Flex
  • XML
  • 编程技巧
  • Android
  • swift
  • C#教程
  • vb
  • vb.net
  • C语言
  • Java
  • Delphi
  • 易语言
  • vc/mfc
  • 嵌入式开发
  • 游戏开发
  • ios
  • 编程问答
  • 汇编语言
  • 微信小程序
  • 数据结构
  • OpenGL
  • 架构设计
  • qt
  • 微信公众号

最近更新的内容

    • C++ 中重载和运算符重载加号实现矩阵相加实例代码
    • C 语言指针变量详细介绍
    • 深入分析C++中deque的使用
    • 生成随机数rand函数的用法详解
    • C语言判断字符是否为可打印字符的方法
    • 数据结构 哈希表设计
    • 浅析结束程序函数exit, _exit,atexit的区别
    • 举例剖析C++中引用的本质及引用作函数参数的使用
    • C语言中使用lex统计文本文件字符数
    • 有关C++继承与友元、继承与类型转换详解

关于我们 - 联系我们 - 免责声明 - 网站地图

©2020-2025 All Rights Reserved. linkedu.com 版权所有