新秀系列C/C++经典问题(六)
类包含一个指向成员复制
称号:下面是类和执行的阵列的声明。题。并针对存在的问题提出几种解决方式。
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];
else
return 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 == ©) // 自赋值检查
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;
请指出错误, 转载请注明出处, 谢谢!
版权声明:本文博主原创文章。博客,未经同意不得转载。
新秀系列C/C++经典问题(六)的更多相关文章
- 新秀系列C/C++经典问题(四)
一个主题:查找最小的k个元素 输入n个整数.输出当中最小的k个. . 分析:这道题最简单的思路莫过于把输入的n个整数排序,这样排在最前面的k个数就是最小的k个数. 仅仅是这样的思路的时间复杂度为O(n ...
- 计算广告CTR预估系列(七)--Facebook经典模型LR+GBDT理论与实践
计算广告CTR预估系列(七)--Facebook经典模型LR+GBDT理论与实践 2018年06月13日 16:38:11 轻春 阅读数 6004更多 分类专栏: 机器学习 机器学习荐货情报局 版 ...
- Linux Shell系列教程之(十六) Shell输入输出重定向
本文是Linux Shell系列教程的第(十六)篇,更多Linux Shell教程请看:Linux Shell系列教程 Shell中的输出和输入的重定向是在使用中经常用到的一个功能,非常实用,今天就为 ...
- Django 系列博客(十六)
Django 系列博客(十六) 前言 本篇博客介绍 Django 的 forms 组件. 基本属性介绍 创建 forms 类时,主要涉及到字段和插件,字段用于对用户请求数据的验证,插件用于自动生成 h ...
- C#入门经典 第六章 委托
C#入门经典 第六章 6.6 委托的声明非常类似于函数,但不带函数体,且要使用delegate关键字. 委托的声明指定了一个返回类型和一个参数列表. 在定义了委托后,就可以声明该委托类型的变量. 接着 ...
- 《手把手教你》系列基础篇(九十六)-java+ selenium自动化测试-框架之设计篇-跨浏览器(详解教程)
1.简介 从这一篇开始介绍和分享Java+Selenium+POM的简单自动化测试框架设计.第一个设计点,就是支持跨浏览器测试. 宏哥自己认为的支持跨浏览器测试就是:同一个测试用例,支持用不同浏览器去 ...
- 【数据结构与算法】多种语言(VB、C、C#、JavaScript)系列数据结构算法经典案例教程合集目录
目录 1. 专栏简介 2. 专栏地址 3. 专栏目录 1. 专栏简介 2. 专栏地址 「 刘一哥与GIS的故事 」之<数据结构与算法> 3. 专栏目录 [经典回放]多种语言系列数据结构算法 ...
- 【JVM】JVM系列之内存模型(六)
一.前言 经过前面的学习,我们终于进入了虚拟机最后一部分的学习,内存模型.理解内存模型对我们理解虚拟机.正确使用多线程编程提供很大帮助.下面开始正式学习. 二.Java并发基础 在并发编程中存在两个关 ...
- [Asp.net 开发系列之SignalR篇]专题六:使用SignalR实现消息提醒
一.引言 前面一篇文章我介绍了如何使用SignalR实现图片的传输,然后对于即时通讯应用来说,消息提醒是必不可少的.现在很多网站的都有新消息的提醒功能.自然对于SignalR系列也少不了这个功能的实现 ...
随机推荐
- VSTO学习笔记(十四)Excel数据透视表与PowerPivot
原文:VSTO学习笔记(十四)Excel数据透视表与PowerPivot 近期公司内部在做一种通用查询报表,方便人力资源分析.统计数据.由于之前公司系统中有一个类似的查询使用Excel数据透视表完成的 ...
- hdu 1251 统计难题 (map水过)
# include <stdio.h> # include <algorithm> # include <string> # include <map> ...
- 用Python的Tkinter实现时钟
import Tkinter,sys,time root=Tkinter.Tk() root.minsize(, ) Label1=Tkinter.Label(text=time.strftime(' ...
- C++封装SQLite实例<三>
前一篇博客中介绍的是怎样依据sqlite3_get_table()函数来获取一张表的内容,就是一股脑的把表中的内容所有存储起来放在一个一维数组中,这其中的规则已经介绍过了.接下来讲的是怎样依据一个SQ ...
- POJ 3632 Optimal Parking(简单题)
[题意简述]:就是选择一个停车地点.然后走遍全部的store后,再回到停车地点.就是走一圈.问要走的距离是多少. [分析]:能够直接求出距离的最大值与最小值,求出差值.乘以2就是最后的输出结果. // ...
- python基础课程_2学习笔记3:图形用户界面
图形用户界面 丰富的平台 写作Python GUI程序前,须要决定使用哪个GUI平台. 简单来说,平台是图形组件的一个特定集合.能够通过叫做GUI工具包的给定Python模块进行訪问. 工具包 描写叙 ...
- VMware vSphere 服务器虚拟化之二十七桌面虚拟化之View中使用Thinapp软件虚拟化
VMware vSphere 服务器虚拟化之二十七桌面虚拟化之View中使用Thinapp软件虚拟化 VMware ThinApp 应用程序虚拟化软件是无代理解决方案,通过将应用程序隔离并封装为EXE ...
- Knockout获取数组元素索引的2种方法,在MVC中实现
原文:Knockout获取数组元素索引的2种方法,在MVC中实现 在遍历数组.集合的时候,通常要获取元素的索引,本篇体验使用Knockout获取索引的2种方法. 假设有这样的一个模型: namespa ...
- HashMap源码解读(转)
http://www.360doc.com/content/10/1214/22/573136_78188909.shtml 最近朋友推荐的一个很好的工作,又是面了2轮没通过,已经是好几次朋友内推没过 ...
- apache2.4.4启用deflate压缩
今天在看<高性能php应用开发>这本书,说道如何启用mod_deflate: 启用如下模块: LoadModule deflate_module modules/mod_deflate.s ...