operator=与复制操作
来源:互联网 发布:盘石网络 编辑:程序博客网 时间:2024/06/01 07:19
4.3复制操作
让你陷入麻烦的并非你所不知的,
而是你所确信的并非如你所知。
--马克.吐温
每个复杂问题都有一个清晰、简单但错误的答案。
--亨利.路易斯.门肯
要想让别人听得明白,言辞必须简洁。
--西塞罗
在复制对象前,应了解对象数据成员的值的构成。对象的存储单元不仅是值的内存类型分配单元,而且包括动态内存,原因是指针(资源句柄)与所标识的内存单元相分离。因此表达式x=y在复制时,不仅要拷贝对象y的静态值,而且对象x要申请新的动态内存,拷贝y的动态值。
因此,当类拷贝一个对象时,必须满足两个准则:
(1)左值对象和右值对象的独立性。在x=y操作之后,对x的操作不会隐藏地改变y的值。对象x,y的静态值和动态内存单元都彼此独立,x和y各自有全面而且独立的地址单元范围。因此,对象x和y不能有指针指向相同的动态单元。
(2)左值对象和右值对象复制后的等价性。在x=y之后,对x和y执行相同的操作或普通函数,得到的结果应相同。
拷贝的实现方法有浅拷贝(或位拷贝)和深拷贝。编译器默认实体
浅拷贝(shallow copy)将使两个对象处于共享状态(shared state)。浅拷贝是按数据成员复制,实际上是对象的数据成员值的复制,而资源没有分配。若数据成员动态分配内存,则出现左值和右值对象x,y都有一个标识相同数据的指针(地址),即x和y在共享状态。因此,左值对象x修改指针所标识的数据,则右值对象y将反映这个修改。这就是拷贝后的不独立。浅拷贝将产生两次(多次)删除和悬挂指针(darling pointer)的问题,还可能出现内存泄漏的问题。但是在多线程中,对象利用同步机制共享数据和资源,因此此时的共享状态是有益的,可用引用计数(或共享数据标志)和写前拷贝解决重复删除问题。
浅拷贝实现方式中的不安全允许类提供显式(explicit)的复制成员函数。深拷贝(deep copy)是对象的所有相关内存和资源的复制,可防止浅拷贝的所有问题。
4.3.1复制构造函数
4.3.2复制赋值运算符
1.operator=
C++中没有追加和修补动态内存的概念,若一个对象执行operator=,将改写对象的旧值,必须先释放所有动态内存,再分配资源句柄成员的新的内存单元。所以赋值操作分配新的资源和数据,使对象“再生”。显式定义或重载operator=,先析构旧资源可防止内存泄漏(图1),再复制分配独立的新资源可防止两次删除(图2)。因为此机制,operator=不能出现自赋值,不能对删除的数据进行拷贝。
operator=的形参类型是对象引用的常量,而且返回类型是同类对象的引用。operator=只有一个形参,是赋值表达式右值对象的引用,然而返回值类型也只能是引用。原因是若将operator=的返回值作为复制复制操作符函数的参数,则返回值必须是对象引用,这是返回值和返回引用不同的问题。例如x=y=z,而operator=是右边优先,因此y=z的返回值是y的引用,应注意不是z的引用。若不返回引用则x。operator=的实现语法是:thing& operator=(const thing&)
算法CA(复制赋值操作符)这个算法的输入是右值对象的引用,将实参的数据成员值和完整的资源句柄的内容复制到左值对象,输出左值对象this的引用。
CA1[防止自赋值]条件语句判断this是否与参数对象相等。
CA2[析构旧资源] delete内存单元或回收旧的资源句柄。
CA3[复制生成新的资源和数据] new申请分配内存单元,复制右
值对象的数据成员和资源句柄的内容。
2.示例
此例是复制二维数组。电子表格是一个由“单元格”(cell)组成的二维表格,每个单元格包含一个数字或字符串。如Mocrosoft Excel提供了完成数学运算的功能。此例并不打算和微软抢市场份额,只是用来说明operator=的用法。
电子表格应用使用了两个基本的类:Spreadsheet和SpreadsheetCell。每个Spreadsheet对象包含多个SpreadsheetCell对象。另外,一个SpreadsheetApplication类管理多个Spreadsheet类。
Spreadsheet是SpreadsheetCell的一个二维数组,提供了方法设置和获取Spreadsheet中特定位置的单元格,在两个方向都使用数字。
#include“SpreadsheetCell.h”
class Spreadsheet
{
public:
Spreadsheet(int inwidth,int inWtight);
~Spreadsheet();
Spreadsheet Spreadsheet(const Spreadsheet& src);
Spreadsheet& operator=(const Spreadsheet& rhs);
void setCellAt(int x,int y,const SpreadsheetCell& cell);
SpreadsheetCell getCellAt(int x,int y);//x,y表示两个方向
protected:
bool inRange(int val,int upper);
int mWidth,mHeight;
SpreadsheetCell **mCells;//mCells相当于二维数组
}
以下是复制构造函数的定义。复制构造函数必须先申请分配动态内存。mCells二维数组是mWidth个一维数组构成,每个一维数组用mCells[i]表示。每个一维数组有相同的mHeight个元素,每个元素用mCells[i][j]表示。mCells[k]表示第(k-1)个一维数组,包括所有mHeight个元素{mCells[k][0],...,mCells[k][mHeight-1]}。指针mCells表示所有一维数组的资源句柄组成的一维数组,mCells指针仍然是一维数组,有mWeight个元素,似乎与C语言不同。见图3。可知,C++的二维指针和二维数组的准则相同,都是一维数组的元素个数必须相同。其次用深复制方式,按元素复制,用两层循环访问每一个元素。
Spreadsheet Spreadsheet(const Spreadsheet& src)//复制构造函数
{
int i,j;
mWidth=src.mWidth;
mHeight=src.mHeight;
mCells=new SpreadsheetCells*[mWidth];
//申请一维数组元素个数mWidth
for(i=0;i<mWidth;i++)//每一个一维数组申请mHeight个元素
mCells[i]=new SpreadsheetCells[mHeight];
for(i=0;i<mWidth;i++)//二维数组按元素复制,访问每一个元素
for(j=0;j<mHeight;i++)
mCells[i][j]=src.mCells[i][j];
}
以下是Spreadsheet类赋值操作符的实现。赋值时,每一个对象应已经得到初始化。所以,在分配新资源前,必须先释放所有动态内存,再分配新资源。
Spreadsheet& operator=(const Spreadsheet& rhs);
{
int i,j;
if(this=&rhs) //自复制
return(*this);
for(i=0;i<mWidth;i++)//收回mWidth个一维数组
delete[] mCell[i];
delete[] mCells;//收回mCells二维数组的所有资源句柄
mWidth=rhs.mWidth;//以下源码与复制构造函数相同
mHeight=rhs.mHeight;
mCells=new SpreadsheetCells*[mWidth];
for(i=0;i<mWidth;i++)
mCells[i]=new SpreadsheetCells[mHeight];
for(i=0;i<mWidth;i++)
for(j=0;j<mHeight;i++)
mCells[i][j]=rhs.mCells[i][j];
return (*this);//必须返回,operator=并不仅仅是给成员数据赋值
}
- operator=与复制操作
- operator= 复制操作符的意外
- 复制操作符“operator=”注意事项
- 论C++中复制构造函数与operator=
- C++ 操作符重载 operator = 、operator <
- C++ 操作符重载 operator = 、operator <
- 重载->和*操作符之operator->()与operator*()
- 复制构造函数和operator=
- 重载赋值操作operator=()
- operator 与&operator
- C++ 类型转换操作与操作符重载 operator type() 与 type operator()
- operator==( )与forward_list
- Python 布尔操作(and/or,Boolean operator)与位操作(&/|,Bitwise operator)
- 为什么operator=操作符返回引用
- C++的构造函数, 复制构造函数 和operator =
- 复制构造函数和operator=的一点注意事项
- operator new与new operator
- new operator与operator new
- (不再跟新,markdown在移动端体验太差)spring , spring的事务管理 , 为什么要事务管理
- Sudoku
- 机器学习七 回归分析
- 李超线段树
- LMS算法学习总结
- operator=与复制操作
- 输入一棵二叉树,求该树的深度。从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。
- JAVA 攻城狮 第二十九天
- 浅谈整体二分
- maven的模块xml配置
- iOS基础之路之addsubview和insertSubView
- bzoj1338: Pku1981 Circle and Points单位圆覆盖
- poj 3461
- 跨域源资源共享CORS