版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/haimianjie2012/article/details/81277320
为什么在自定义类时,需要重写拷贝赋值函数和拷贝构造函数?
1.理解这个问题前,先要弄明白深拷贝和浅拷贝两个概念:
如果一个类拥有资源,当这个类的对象发生复制过程的时候,资源重新分配,这个过程就是深拷贝。
反之,没有重新分配资源,只是对对象中的数据成员进行简单的赋值,就是浅拷贝。默认拷贝构造函数执行的是浅拷贝。
有了浅拷贝为什么还需要浅拷贝?
在某些状况下,类内成员变量需要动态开辟堆内存,如果实行浅拷贝,也就是把对象里的值完全复制给另一个对象,如A=B。这时,错误就来了。
class Test{
public:
Test(const char *str = "");//构造函数
Test(const Test& copy); //拷贝构造函数
Test& operator = (const Test& assign); //拷贝构造函数
~Test(); //析构函数
private:
size_t m_size; //字符串的长度
char *m_data; //字符串指针
} ;
以类Test的两个对象a、b为例,假设a.m_data的内容为“hi”,a.m_data的内容为“hello”。
i、将a赋值给b,即b.m_data = a.m_data。这就会造成错误,如下图所示:
1、b.m_data 原有内存没有释放,造成内存泄露。
2、b.m_data 和 a.m_data指向了同一块内存,任何一方变动都会影响另一方。
3、对象析构时,m_data析构了两次。
ii、拷贝构造也是同样的道理
两个成员变量指针也指向同一块内存。当B把内存释放了(如:析构),这时A内的指针就是野指针了,出现运行错误。
2.C++在以下三种情况下,需要调用拷贝构造函数:
一个对象以值传递的方式传入函数体
一个对象以值传递的方式从函数返回
一个对象需要通过另外一个对象进行初始化。
注:
A a(a1);//一个对象需要通过另外一个对象进行初始化,调用拷贝构造函数
a3=a2;//把一个对象赋值给另外一个已经存在的对象,调用拷贝赋值函数
拷贝构造函数与拷贝赋值函数的区别和具体实现方式参见下面代码:
#pragma once
#include<vector>
using namespace std;
class MyPoint
{
public:
MyPoint();
~MyPoint();
MyPoint(const MyPoint& copy); //拷贝构造函数
MyPoint& operator = (const MyPoint& assign); //拷贝构造函数
public:
int mindex;
POINT* lineData;
int width;
};
class MyImg
{
public:
MyImg();
~MyImg();
MyImg(const MyImg& copy); //拷贝构造函数
MyImg& operator = (const MyImg& assign); //拷贝构造函数
public:
int mNum;
int width;
int height;
int** mimg;
vector<MyPoint> mvLine;
};
#include "stdafx.h"
#include "MyPoint.h"
MyPoint::MyPoint()
{
lineData = NULL;
}
MyPoint::~MyPoint()
{
if (lineData!=NULL)
{
delete[] lineData;
}
}
//拷贝构造函数
MyPoint::MyPoint (const MyPoint& copy)
{
mindex = copy.mindex;
width = copy.width;
lineData = new POINT[width];
for (int i = 0; i < width; i++)
{
lineData[i].x = copy.lineData[i].x;
lineData[i].y= copy.lineData[i].y;
}
}
//拷贝构造函数
MyPoint& MyPoint::operator = (const MyPoint& assign)
{
if (this != &assign)
{
mindex = assign.mindex;
width = assign.width;
POINT* tm = new POINT[width];
for (int i = 0; i < width; i++)
{
tm[i].x = assign.lineData[i].x;
tm[i].y = assign.lineData[i].y;
}
delete [] lineData;
lineData = tm;
}
return *this;
}
MyImg::MyImg()
{
mNum = 0;
mimg = NULL;
}
MyImg::~MyImg()
{
if (mimg != NULL)
{
delete[] mimg;
mimg = NULL;
}
mvLine.clear();
}
//拷贝构造函数
MyImg::MyImg(const MyImg& copy)
{
mNum = copy.mNum;
width = copy.width;
height = copy.height;
mimg = new int*[width];
for (int i = 0; i < width; i++)
{
mimg[i] = new int[height];
for (int j = 0; j < height; j++)
{
mimg[i][j] = copy.mimg[i][j];
}
}
mvLine.clear();
for (int i = 0; i < copy.mvLine.size(); i++)
{
MyPoint mPoint;
mPoint.mindex = copy.mvLine[i].mindex;
mPoint.width = copy.mvLine[i].width;
mPoint.lineData = new POINT[width];
for (int j = 0; j < width; j++)
{
mPoint.lineData[j].x = copy.mvLine[i].lineData[j].x;
mPoint.lineData[j].y = copy.mvLine[i].lineData[j].y;
}
mvLine.push_back(mPoint);
}
}
//拷贝构造函数
MyImg& MyImg::operator = (const MyImg& assign)
{
if (this != &assign)
{
mNum=assign.mNum;
width = assign.width;
height = assign.height;
int** tmimg=new int*[width];
for (int i = 0; i < width; i++)
{
tmimg[i] = new int[height];
for (int j = 0; j < height; j++)
{
tmimg[i][j] = assign.mimg[i][j];
}
}
for (int i = 0; i < width; i++)
{
delete[] mimg[i];
}
delete[] mimg;
mimg = tmimg;
mvLine.clear();
for (int i = 0; i < assign.mvLine.size(); i++)
{
MyPoint mPoint ;
mPoint.mindex = assign.mvLine[i].mindex;
mPoint.width = assign.mvLine[i].width;
mPoint.lineData = new POINT[width];
for (int j = 0; j < width; j++)
{
mPoint.lineData[i].x = assign.mvLine[i].lineData[j].x;
mPoint.lineData[i].y = assign.mvLine[i].lineData[j].y;
}
mvLine.push_back(mPoint);
}
}
return *this;
}
vector传参的问题:
1.vector的几种传参方式测试
//引用和指针都可传回来值
void AddTestVector::ReturnVector(vector<MyPoint> vPoint, vector<MyPoint> & mvPoint, vector<MyPoint*> pvPoint)
{
for (int i = 0; i < vPoint.size(); i++)
{
for (int j = 0; j < vPoint[i].width; j++)
{
vPoint[i].lineData[j].x = 5;
vPoint[i].lineData[j].y = 2;
}
}
for (int i = 0; i < mvPoint.size(); i++)
{
for (int j = 0; j < mvPoint[i].width; j++)
{
mvPoint[i].lineData[j].x = 5;
mvPoint[i].lineData[j].y = 2;
}
}
for (int i = 0; i < pvPoint.size(); i++)
{
for (int j = 0; j < pvPoint[i]->width; j++)
{
pvPoint[i]->lineData[j].x = 5;
pvPoint[i]->lineData[j].y = 2;
}
}
}
void AddTestVector::TestVectorOne()
{
vector<MyPoint> vPoint;
vector<MyPoint> mvPoint;
vector<MyPoint*> pvPoint;
MyPoint myPnt;
myPnt.width = 5;
myPnt.mindex = 1;
myPnt.lineData = new POINT[myPnt.width];
for (int i = 0; i < myPnt.width; i++)
{
myPnt.lineData[i].x = 1;
myPnt.lineData[i].y = 1;
}
vPoint.push_back(myPnt);
mvPoint.push_back(myPnt);
pvPoint.push_back(&myPnt);
ReturnVector(vPoint, mvPoint, pvPoint);
int wid1 = vPoint[0].width;
int index = vPoint[0].mindex;
for (int i = 0; i < wid1; i++)
{
int vpx = vPoint[0].lineData[i].x;//x=1,并没有改变值
int vpy = vPoint[0].lineData[i].y;//y=1,
int mvpx = mvPoint[0].lineData[i].x;//x=5
int mvpy = mvPoint[0].lineData[i].y;//y=2
int pvx = pvPoint[0]->lineData[i].x;//x=5
int pvy = pvPoint[0]->lineData[i].y;//y=2
}
}
2.vecotor包含对象中包含vector时如何将改变的值传出来
void AddTestVector::process_mul_layer_line(vector<MyImg> &curImg)
{
vector<MyImg> tmImg=curImg;//调用MyImg的拷贝构造函数,tmImg,curImg指向两个不同地址
curImg.clear();//此时,curImg的size为0,tmImg的size没有改变
for (int i = 0; i < tmImg.size(); i++)
{
MyImg tm = tmImg[i];
for (int k = 0; k < tm.mvLine.size(); k++)
{
for (int j = 0; j < tm.width; j++)
{
tm.mvLine[k].lineData[j].x =5;
tm.mvLine[k].lineData[j].y = 3;
}
}
curImg.push_back(tm);
}
}
void AddTestVector::TestImg()
{
MyImg curImg;
curImg.mNum = 1;
curImg.width = 3;
curImg.height =4;
curImg.mimg = new int*[curImg.width];
for (int i = 0; i < curImg.width; i++)
{
curImg.mimg[i] = new int[curImg.height];
for (int j = 0; j < curImg.height; j++)
{
curImg.mimg[i][j] = 1;
}
}
MyPoint myPnt;
myPnt.width = 5;
myPnt.mindex = 1;
myPnt.lineData = new POINT[myPnt.width];
for (int i = 0; i < myPnt.width; i++)
{
myPnt.lineData[i].x = 1;
myPnt.lineData[i].y = 1;
}
curImg.mvLine.push_back(myPnt);
vector<MyImg> curImgs;
curImgs.push_back(curImg);
process_mul_layer_line(curImgs);
for (int i = 0; i < curImgs.size(); i++)
{
MyImg tm = curImgs[i];
for (int k = 0; k < tm.mvLine.size(); k++)
{
for (int j = 0; j < tm.width; j++)
{
//x,y的值已经改变,x=5,y=3
printf("第一个 x:%d,y:%d", tm.mvLine[k].lineData[j].x,tm.mvLine[k].lineData[j].y);
}
}
}
}