题目:下面是一个数组类的声明与实现。请分析这个类有什么问题,并针对存在的问题提出几种解决方案。

template<typename T> class Array
{
public:Array(unsigned arraySize):data(0), size(arraySize){if(size > 0)data = new T[size];}~Array(){if(data) delete[] data;}void setValue(unsigned index, const T& value){if(index < size)data[index] = value;}T getValue(unsigned index) const{if(index < size)return data[index];elsereturn T();}private:T* data;unsigned size;
};

分析:我们注意在类的内部封装了用来存储数组数据的指针。软件存在的大部分问题通常都可以归结指针的不正确处理。

这个类只提供了一个构造函数,而没有定义构造拷贝函数和重载拷贝运算符函数。当这个类的用户按照下面的方式声明并实例化该类的一个实例

Array A(10);
Array B(A);

或者按照下面的方式把该类的一个实例赋值给另外一个实例

Array A(10);
Array B(10);
B=A;

编译器将调用其自动生成的构造拷贝函数或者拷贝运算符的重载函数。在编译器生成的缺省的构造拷贝函数和拷贝运算符的重载函数,对指针实行的是按位拷贝,仅仅只是拷贝指针的地址,而不会拷贝指针的内容。因此在执行完前面的代码之后,A.data和B.data指向的同一地址。当A或者B中任意一个结束其生命周期调用析构函数时,会删除data。由于他们的data指向的是同一个地方,两个实例的data都被删除了。但另外一个实例并不知道它的data已经被删除了,当企图再次用它的data的时候,程序就会不可避免地崩溃。

由于问题出现的根源是调用了编译器生成的缺省构造拷贝函数和拷贝运算符的重载函数。一个最简单的办法就是禁止使用这两个函数。于是我们可以把这两个函数声明为私有函数,如果类的用户企图调用这两个函数,将不能通过编译。实现的代码如下:

private:Array(const Array& copy);const Array& operator = (const Array& copy); 

最初的代码存在问题是因为不同实例的data指向的同一地址,删除一个实例的data会把另外一个实例的data也同时删除。因此我们还可以让构造拷贝函数或者拷贝运算符的重载函数拷贝的不只是地址,而是数据。由于我们重新存储了一份数据,这样一个实例删除的时候,对另外一个实例没有影响。这种思路我们称之为深度拷贝。实现的代码如下:

public:Array(const Array& copy):data(0), size(copy.size){if(size > 0){data = new T[size];for(int i = 0; i < size; ++ i)setValue(i, copy.getValue(i));}}const Array& operator = (const Array& copy){if(this == &copy)return *this;if(data != NULL){delete []data;data = NULL;}size = copy.size;if(size > 0){data = new T[size];for(int i = 0; i < size; ++ i)setValue(i, copy.getValue(i));}}

为了防止有多个指针指向的数据被多次删除,我们还可以保存究竟有多少个指针指向该数据。只有当没有任何指针指向该数据的时候才可以被删除。这种思路通常被称之为引用计数技术。在构造函数中,引用计数初始化为1;每当把这个实例赋值给其他实例或者以参数传给其他实例的构造拷贝函数的时候,引用计数加1,因为这意味着又多了一个实例指向它的data;每次需要调用析构函数或者需要把data赋值为其他数据的时候,引用计数要减1,因为这意味着指向它的data的指针少了一个。当引用计数减少到0的时候,data已经没有任何实例指向它了,这个时候就可以安全地删除。实现的代码如下:

public:Array(unsigned arraySize):data(0), size(arraySize), count(new unsigned int){*count = 1;if(size > 0)data = new T[size];}Array(const Array& copy): size(copy.size), data(copy.data), count(copy.count){++ (*count);}~Array(){Release();}const Array& operator = (const Array& copy){if(data == copy.data)return *this;Release();data = copy.data;size = copy.size;count = copy.count;++(*count);}private:void Release(){--(*count);if(*count == 0){if(data){delete []data;data = NULL;}delete count;count = 0;}}unsigned int *count; 

博主何海涛对本博客文章享有版权。网络转载请注明出处http://zhedahht.blog.163.com/。整理出版物请和作者联系。

程序员面试题精选100题(15)-含有指针成员的类的拷贝[C/C++/C#]相关推荐

  1. 程序员面试题精选100题

    程序员面试题精选100题(01)-把二元查找树转变成排序的双向链表 题目:输入一棵二元查找树,将该二元查找树转换成一个排序的双向链表.要求不能创建任何新的结点,只调整指针的指向. 比如将二元查找树   ...

  2. [程序员面试题精选100题]13.第一个只出现一次的字符

    [题目] 在一个字符串中找到第一个只出现一次的字符.如输入abaccdeff,则输出b. [分析] [代码] /********************************* * 日期:2013- ...

  3. 程序员面试题精选100题(51)-顺时针打印矩阵

    // 程序员面试题精选100题(51)-顺时针打印矩阵.cpp : 定义控制台应用程序的入口点. //#include "stdafx.h" #include <iostre ...

  4. 程序员面试题精选100题:求从1到n的正数中1出现的次数

    // 程序员面试题精选100题(25):求从1到n的正数中1出现的次数 // 如 f(253) = (2!=0) * 100 + 2 * f(99) + (5!=0) * 10 + 5 * f(9) ...

  5. 程序员面试题精选100题:41-50解题报告

    程序员面试题精选100题(41)-把数组排成最小的数[算法]   题目:输入一个正整数数组,将它们连接起来排成一个数,输出能排出的所有数字中最小的一个.例如输入数组{32,  321},则输出这两个能 ...

  6. 程序员面试题精选100题:11-40解题报告

    程序员面试题精选100题(11)-求二元查找树的镜像[数据结构]   题目:输入一颗二元查找树,将该树转换为它的镜像,即在转换后的二元查找树中,左子树的结点都大于右子树的结点.用递归和循环两种方法完成 ...

  7. [程序员面试题精选100题]19.反转链表

    题目 输入一个链表的头结点,反转该链表,并返回反转后链表的头结点. 分析 假设经过若干操作,我们已经把结点 pre之前的指针调整完毕,这些结点的next指针都指向前面一个结点.现在我们遍历到结点cur ...

  8. 程序员面试题精选100题(03)-子数组的最大和[算法]

    题目:输入一个整形数组,数组里有正数也有负数.数组中连续的一个或多个整数组成一个子数组,每个子数组都有一个和.求所有子数组的和的最大值.要求时间复杂度为O(n). 例如输入的数组为1, -2, 3, ...

  9. python程序员面试题精选100题_在Python程序员面试中被问的最多的10道题

    我们在为大家整Python程序员面试试题中,发现了一些被面试官问到的最多的一些问题,以下就是本篇内容: Python是个非常受欢迎的编程语言,随着近些年机器学习.云计算等技术的发展,Python的职位 ...

最新文章

  1. Linux内核编译学习1
  2. 自然语言系列学习之表示学习与知识获取(七)利用关系路径进行关系抽取
  3. bert 中文 代码 谷歌_ELECTRA中文预训练模型开源,110个参数,性能媲美BERT
  4. android动态申请悬浮框权限,Android创建悬浮窗的完整步骤
  5. 《恋上数据结构第1季》二叉堆原理及实现、最小堆解决 TOP K 问题
  6. Linux 命令源码 —— cat
  7. pb 如何导出csv_如何巧用长投温度定投指数基金
  8. 仿真树叶飘落效果的实现(精灵旋转…
  9. RLC串联谐振那些事
  10. python之bug0:selenium使用新版edge(chrome内核) 导致的webdriver.Edge 运行报错
  11. Tuner及工作原理介绍
  12. 单双号限行微信小程序源码
  13. 千里马常有而伯乐不常有
  14. jQuery显示隐藏
  15. 技术专题:XML,DB2数据库的黄金甲
  16. 复杂社会网络传播模式研究项目申报书
  17. Dobbo问题及解决方案:forbid-consumer
  18. 转载:UBTC(比特联储)的商业应用全景解析
  19. 竞拍价格这个过程——在线拍卖行(2)
  20. 无盘服务器配置网众,网众无盘服务端工作站设置

热门文章

  1. 大数据分析:Java 下降,华为平均月薪高达 35K,分析 89 万招聘数据有这些发现!
  2. TensorFlow贡献者黄文坚:解读对比13个深度学习框架后的选择
  3. docker 安装nginx 配置目录挂载
  4. unable to access ‘https://gitee...‘: Failed to connect to 127.0.0.1 port 31181 after 2029 ms: Connec
  5. android studio引用module出的错:Unable to resolve dependency for‘:app@debug/........
  6. Halcon算子盘点:Chapter 16:System
  7. cmd oracle 连接实例_基于winserver的Oracle数据库跨版本下的rman备份恢复
  8. 微信小程序勾选协议与提交按钮联动
  9. rgb颜色分量的旋转_RGB分量之偏色与彩色图片转黑白
  10. mysql创建非聚集索引_一文看懂聚集索引和非聚集索引的区别