C++深层复制对象
C++ Deep Copy Object
我正在尝试来回深度复制对象。当我运行 gdb 时,在循环迭代后出现以下错误。
Program received signal SIGSEGV, Segmentation fault.
0x0804ab96 in DGCPM::DGCPM (this=0x844b760, cur=0x1) at DGCPM.C:27
27 memcpy(vRCells, cur->vRCells,sizeof(float)*nThetaCells);
我怀疑问题与创建"新类"有关,但我不确定。有什么建议吗?
(注意:"_initialize"代码调用在程序中设置值的 FORTRAN 子例程。
这是运行。C 主文件:
#include "../include/DGCPM.h"
#define particle_num 5
class DGCPM **mallocModels(int n);
int main(int argc, char *argv[]){
class DGCPM **m;
class DGCPM **cur;
m=mallocModels(particle_num);//update
for(int t = 0; t < 48; t++){
//Update m, and then...
cur = m;
m = (DGCPM**)malloc(sizeof(class DGCPM *)*particle_num);
for(int i=0;i<particle_num;i++){
randomidx = ((double)rand() / ((double)RAND_MAX + 1));
currentidx = find(cumPw,randomidx,particle_num);
m[i] = new class DGCPM(cur[currentidx]);
}
for(int i=0;i<particle_num;i++){
delete cur[i];
}
free(cur);
}
return 0;
}
/*============================================================================
mallocModels - allocate the ensemble of models
============================================================================*/
class DGCPM **mallocModels(int n){
class DGCPM **m;
m=(class DGCPM **)amjSafeMalloc(sizeof(class DGCPM *)*n,
(char *)"mallocModels:m");
for(int i=0;i<n;i++)
m[i]=new class DGCPM();
return m;
}
/*============================================================================
Find - Return a particle index that has a high probability of having a high weight.
============================================================================*/
int find(float *cumPw, double randomidx, int nM){
/*Wrong implementation*/
int index = 0;
flag = 0;
while(flag == 0){
if(cumPw[i] >= randomidx){
flag = 1;
i++;
}
else{
index ++;
}
}
return index; //Sometimes, index was going to number of models, or number of models + 1, which are out of bounds.
/*Correct implementation*/
int index = 0;
for(int i = 0; i < nM-1; i++){
if(cumPw[i] >= randomidx){
index = i;
break;
}
}
if(index >= nM){
index = nM-1;
printf("Error: random index exceeds bounds");
}
return index;
}
下面是 DGCPM.h 头文件:
class DGCPM{
public:
DGCPM(); /* Initialized with defaults setup */
DGCPM(class DGCPM *cur); //Copy constructor
DGCPM(int nThetaCells, int nPhiCells, float thetaMin, float thetaMax);
~DGCPM(); /* Free memory */
private:
int internal; /* 1=memory allocated internally and should be deallocated when ~DGCPM is called, 2=memory is internal except for mGridN which is external */
int nThetaCells,nRCells,nPhiCells;
float thetaMin,thetaMax;
float rMin,rMax;
float delR,delPhi;
float deltMax;
float *vRCells; /* [nThetaCells] */
float *vThetaCells; /* [nThetaCells] */
float *vPhiCells; /* [nPhiCells] */
float **mGridB; /* [nPhiCells][nThetaCells] */
float **mGridBi; /* [nPhiCells][nThetaCells] */
float **mGridPot; /* [nPhiCells][nThetaCells] */
float **mGridEr; /* [nPhiCells][nThetaCells] */
float **mGridEp; /* [nPhiCells][nThetaCells] */
float **mGridVr; /* [nPhiCells][nThetaCells] */
float **mGridVp; /* [nPhiCells][nThetaCells] */
float **mGridN; /* [nPhiCells][nThetaCells] */
float **mGridHalf; /* [nPhiCells][nThetaCells] Particles / weber (workspace for upwind and superbee) */
float **mGridDen; /* [nPhiCells][nThetaCells] */
float **mGridVol; /* [nPhiCells][nThetaCells] */
float **mGridX; /* [nPhiCells][nThetaCells] */
float **mGridY; /* [nPhiCells][nThetaCells] */
float **mGridOc; /* [nPhiCells][nThetaCells] */
float **std; /* [nPhiCells][nThetaCells] */
float parI[2];
float delTMax;
float Re;
void initialize(int nThetaCells, int nPhiCells, float thetaMin,
float thetaMax);
};
最后是DGCPM。C 对象包装器:
/******************************************************************************
* DGCPM.C - This implements the DGCPM plasmasphere model class *
******************************************************************************/
#define TWO_PI 6.2831853071795864769252866
#include "../include/DGCPM.h"
# include <cstdlib>
# include <cmath>
/*============================================================================
DGCPM::DGCPM()
Initialize with default setup
============================================================================*/
DGCPM::DGCPM(){
internal=1;
initialize(200,200,14.963217,60.0);/*(180,200,14.963217,60.0);*/
}
//Copy Constructor
DGCPM::DGCPM(class DGCPM *cur){
internal=1;
initialize(200,200,14.963217,60.0);/*(180,200,14.963217,60.0);*/
memcpy(vRCells, cur->vRCells,sizeof(float)*nThetaCells);
memcpy(vPhiCells, cur->vPhiCells,sizeof(float)*nPhiCells);
memcpy(vThetaCells, cur->vThetaCells,sizeof(float)*nThetaCells);
memcpy(mGridB[0], cur->mGridB[0],sizeof(float)*nThetaCells*nPhiCells);
memcpy(mGridBi[0], cur->mGridBi[0],sizeof(float)*nThetaCells*nPhiCells);
memcpy(mGridPot[0], cur->mGridPot[0],sizeof(float)*nThetaCells*nPhiCells);
memcpy(mGridEr[0], cur->mGridEr[0],sizeof(float)*nThetaCells*nPhiCells);
memcpy(mGridEp[0], cur->mGridEp[0],sizeof(float)*nThetaCells*nPhiCells);
memcpy(mGridVr[0], cur->mGridVr[0],sizeof(float)*nThetaCells*nPhiCells);
memcpy(mGridVp[0], cur->mGridVp[0],sizeof(float)*nThetaCells*nPhiCells);
memcpy(mGridN[0], cur->mGridN[0],sizeof(float)*nThetaCells*nPhiCells);
memcpy(mGridHalf[0], cur->mGridHalf[0],sizeof(float)*nThetaCells*nPhiCells);
memcpy(mGridDen[0], cur->mGridDen[0],sizeof(float)*nThetaCells*nPhiCells);
memcpy(mGridVol[0], cur->mGridVol[0],sizeof(float)*nThetaCells*nPhiCells);
memcpy(mGridOc[0], cur->mGridOc[0],sizeof(float)*nThetaCells*nPhiCells);
memcpy(mGridX[0], cur->mGridX[0],sizeof(float)*nThetaCells*nPhiCells);
memcpy(mGridY[0], cur->mGridY[0],sizeof(float)*nThetaCells*nPhiCells);
memcpy(std[0], cur->std[0],sizeof(float)*nThetaCells*nPhiCells);
}
/*============================================================================
DGCPM::~DGCPM()
Free allocated memory
============================================================================*/
DGCPM::~DGCPM(){
if(internal>=1){
amjFree1dFloat(vRCells);
amjFree1dFloat(vThetaCells);
amjFree1dFloat(vPhiCells);
amjFree2dFloat(mGridB);
amjFree2dFloat(mGridBi);
amjFree2dFloat(mGridEr);
amjFree2dFloat(mGridEp);
amjFree2dFloat(mGridVr);
amjFree2dFloat(mGridVp);
if(internal==1) amjFree2dFloat(mGridN);
amjFree2dFloat(mGridHalf);
amjFree2dFloat(mGridDen);
amjFree2dFloat(mGridVol);
amjFree2dFloat(mGridX);
amjFree2dFloat(mGridY);
amjFree2dFloat(mGridOc);
amjFree2dFloat(std);
}
}
/******************************************************************************
******************************************************************************
** Private functions **
******************************************************************************
******************************************************************************/
/*============================================================================
DGCPM::initialize(int nThetaCells, int nPhiCells, float thetaMin,
float thetaMax);
This is the initialization function used when all memory should be
allocated internally.
============================================================================*/
void DGCPM::initialize(int nThetaCells, int nPhiCells, float thetaMin,
float thetaMax){
initialize(nThetaCells,nPhiCells,thetaMin,thetaMax,
amjMalloc1dFloat(nThetaCells,(char *)"DGCPM::DGCPM:vRCells"),
amjMalloc1dFloat(nThetaCells,(char *)"DGCPM::DGCPM:vThetaCells"),
amjMalloc1dFloat(nPhiCells,(char *)"DGCPM::DGCPM:vPhiCells"),
amjMalloc2dFloat(nPhiCells,nThetaCells,
(char *)"DGCPM::DGCPM:mGridB"),
amjMalloc2dFloat(nPhiCells,nThetaCells,
(char *)"DGCPM::DGCPM:mGridBi"),
amjMalloc2dFloat(nPhiCells,nThetaCells,
(char *)"DGCPM::DGCPM:mGridPot"),
amjMalloc2dFloat(nPhiCells,nThetaCells,
(char *)"DGCPM::DGCPM:mGridEr"),
amjMalloc2dFloat(nPhiCells,nThetaCells,
(char *)"DGCPM::DGCPM:mGridEp"),
amjMalloc2dFloat(nPhiCells,nThetaCells,
(char *)"DGCPM::DGCPM:mGridVr"),
amjMalloc2dFloat(nPhiCells,nThetaCells,
(char *)"DGCPM::DGCPM:mGridVp"),
amjMalloc2dFloat(nPhiCells,nThetaCells,
(char *)"DGCPM::DGCPM:mGridN"),
amjMalloc2dFloat(nPhiCells,nThetaCells,
(char *)"DGCPM::DGCPM:mGridHalf"),
amjMalloc2dFloat(nPhiCells,nThetaCells,
(char *)"DGCPM::DGCPM:mGridDen"),
amjMalloc2dFloat(nPhiCells,nThetaCells,
(char *)"DGCPM::DGCPM:mGridVol"),
amjMalloc2dFloat(nPhiCells,nThetaCells,
(char *)"DGCPM::DGCPM:mGridX"),
amjMalloc2dFloat(nPhiCells,nThetaCells,
(char *)"DGCPM::DGCPM:mGridY"),
amjMalloc2dFloat(nPhiCells,nThetaCells,
(char *)"DGCPM::DGCPM:mGridOc"),
//Added by J.Wise
amjMalloc2dFloat(nPhiCells,nThetaCells,
(char *)"DGCPM::DGCPM:std"));
}
/*============================================================================
DGCPM::initialize(int nThetaCells, int nPhiCells, float thetaMin,
float thetaMax);
This is the initialization function used when mGridN is passed from
the outside but all other memory is allocated internally.
============================================================================*/
void DGCPM::initialize(int nThetaCells, int nPhiCells, float thetaMin,
float thetaMax, float **mGridN){
initialize(nThetaCells,nPhiCells,thetaMin,thetaMax,
amjMalloc1dFloat(nThetaCells,(char *)"DGCPM::DGCPM:vRCells"),
amjMalloc1dFloat(nThetaCells,(char *)"DGCPM::DGCPM:vThetaCells"),
amjMalloc1dFloat(nPhiCells,(char *)"DGCPM::DGCPM:vPhiCells"),
amjMalloc2dFloat(nPhiCells,nThetaCells,
(char *)"DGCPM::DGCPM:mGridB"),
amjMalloc2dFloat(nPhiCells,nThetaCells,
(char *)"DGCPM::DGCPM:mGridBi"),
amjMalloc2dFloat(nPhiCells,nThetaCells,
(char *)"DGCPM::DGCPM:mGridPot"),
amjMalloc2dFloat(nPhiCells,nThetaCells,
(char *)"DGCPM::DGCPM:mGridEr"),
amjMalloc2dFloat(nPhiCells,nThetaCells,
(char *)"DGCPM::DGCPM:mGridEp"),
amjMalloc2dFloat(nPhiCells,nThetaCells,
(char *)"DGCPM::DGCPM:mGridVr"),
amjMalloc2dFloat(nPhiCells,nThetaCells,
(char *)"DGCPM::DGCPM:mGridVp"),
mGridN,
amjMalloc2dFloat(nPhiCells,nThetaCells,
(char *)"DGCPM::DGCPM:mGridHalf"),
amjMalloc2dFloat(nPhiCells,nThetaCells,
(char *)"DGCPM::DGCPM:mGridDen"),
amjMalloc2dFloat(nPhiCells,nThetaCells,
(char *)"DGCPM::DGCPM:mGridVol"),
amjMalloc2dFloat(nPhiCells,nThetaCells,
(char *)"DGCPM::DGCPM:mGridX"),
amjMalloc2dFloat(nPhiCells,nThetaCells,
(char *)"DGCPM::DGCPM:mGridY"),
amjMalloc2dFloat(nPhiCells,nThetaCells,
(char *)"DGCPM::DGCPM:mGridOc"),
amjMalloc2dFloat(nPhiCells,nThetaCells,
(char *)"DGCPM::DGCPM:std"));
}
/*
initialize() - this initialization function uses pre-allocated
memory areas passed in from the outside. This function is used both
when DGCPM allocates memory itself and when it receives
pre-allocated memory from the outside in order to eliminate
duplication of code with the associated risk of errors.
============================================================================*/
void DGCPM::initialize(int nThetaCells, int nPhiCells, float thetaMin,
float thetaMax, float *vRCells, float *vThetaCells,
float *vPhiCells, float **mGridB, float **mGridBi,
float **mGridPot, float **mGridEr, float **mGridEp,
float **mGridVr, float **mGridVp, float **mGridN,
float **mGridHalf, float **mGridDen, float **mGridVol,
float **mGridX, float **mGridY, float **mGridOc, float **std){
DGCPM::nThetaCells=nThetaCells;
DGCPM::nPhiCells=nPhiCells;
DGCPM::thetaMin=thetaMin;
DGCPM::thetaMax=thetaMax;
DGCPM::vRCells=vRCells;
DGCPM::vThetaCells=vThetaCells;
DGCPM::vPhiCells=vPhiCells;
DGCPM::mGridB=mGridB;
DGCPM::mGridBi=mGridBi;
DGCPM::mGridPot=mGridPot;
DGCPM::mGridEr=mGridEr;
DGCPM::mGridEp=mGridEp;
DGCPM::mGridVr=mGridVr;
DGCPM::mGridVp=mGridVp;
DGCPM::mGridN=mGridN;
DGCPM::mGridHalf=mGridHalf;
DGCPM::mGridDen=mGridDen;
DGCPM::mGridVol=mGridVol;
DGCPM::mGridX=mGridX;
DGCPM::mGridY=mGridY;
DGCPM::mGridOc=mGridOc;
DGCPM::std=std;
Re=6.378e6;
initialize_(&nThetaCells,&nRCells,&nPhiCells,&thetaMin,&thetaMax,&rMin,&rMax,
&delR,&delPhi,vRCells,vThetaCells,vPhiCells,mGridB[0],mGridBi[0],
mGridN[0],mGridDen[0],mGridVol[0],mGridX[0],mGridY[0],mGridOc[0],std[0]);
}
下面是一个示例自定义内存函数,它负责初始化和分配:
void *amjSafeMalloc(int n, char *message){
void *d;
d=malloc(n);
if(d==NULL){
fprintf(stderr,"amjSafeMalloc error: Could not allocate %d bytes "
"for %s. Exiting.n",n,message);
exit(1);
}
return d;
}
float *amjMalloc1dFloat(int a, char *message){
float *d;
sprintf(msg,"%s:amjMalloc1DFloat:d",message);
d=(float *)amjSafeMalloc(sizeof(float)*a,msg);
return d;
}
float **amjMalloc2dFloat(int a, int b, char *message){
float **d;
int i;
sprintf(msg,"%s:amjMalloc2DFloat:d",message);
d=(float **)amjSafeMalloc(sizeof(float *)*a,msg);
sprintf(msg,"%s:amjMalloc2DFloat:d[0]",message);
d[0]=(float *)amjSafeMalloc(sizeof(float)*a*b,msg);
for(i=1;i<a;i++) d[i]=d[i-1]+b;
return d;
}
class DGCPM
{
public:
DGCPM(int nThetaCells, int nPhiCells)
: nThetaCells(nThetaCells)
, nPhiCells(nPhiCells)
, mGridB(nThetaCells, vector<float>(nPhiCells)) // first Y then X
{
}
private:
int nThetaCells, nPhiCells;
vector<vector<float>> mGridB;
};
免费提供深度拷贝。免费删除内存。
免费的意思是你不必编写代码。
从您的评论/* [nPhiCells][nThetaCells] */
您的类定义中,我认为您打算将float**
作为 2D 数组。但是,如果可以像 2D 数组一样使用它们,它们实际上是指向数组的指针数组。这是一个巨大的区别:这意味着,你必须复制nPhiCells
nThetaCells
元素的单个数组,并且你必须设置指针数组本身。现在,当你这样做时
memcpy(mGridHalf[0], cur->mGridHalf[0],sizeof(float)*nThetaCells*nPhiCells);
在复制构造函数中,假定没有指针数组,并且所有线数组在内存中都是顺序的。要么此副本超出指针数组的边界(段错误(,要么通过mGridHalf[i][j]
访问数组只是做了错误的事情,将float
数据重新解释为指针(和段错误(。
不幸的是,C++是一种与 fortran 多维数组交互的可怕语言,因为它没有可变大小数组的概念。所以下面是C代码,而不是C++代码。在 C 中,您可以像这样解决该问题:
float (*mGridHalf)[nThetaCells] = malloc(nPhiCells*sizeof(*mGridHalf));
将正确分配和键入一个 2D 数组(即数组数组(,
可以使用mGridHalf[phi][theta] = 7.3;
由于所有元素在内存中都是连续的,因此可以正确复制整个元素
memcpy(mGridHalf, cur->mGridHalf, nPhiCells*sizeof(*mGridHalf));
并释放
free(mGridHalf);
从技术上讲,mGridHalf
现在是指向数组的指针,数组访问调用的指针算术有效地执行与编写相同的计算
float* foo = malloc(nPhiCells*nThetaCells*sizeof(*foo));
foo[phi*nThetaCells + theta] = 7.3;
但是,使用正确的指针类型 float (*)[nThetaCells]
可以避免自己执行索引计算。
问题很可能是你假设 float** 的数据是一个连续的内存块。 如果是这样,这是实现此目的的一种方法。 首先,我展示了错误的方式(但经常使用(:
float** createFloat2D(int nRows, int nCols)
{
float** p1 = new float*[nRows];
for (int i = 0; i < nCols; ++i )
p1[i] = new float[nCols];
return p1;
}
void destroyFloat2D(float**f, int nRows, int nCols)
{
for (int i = 0; i < nCols; ++i )
delete [] f[i];
delete [] f;
}
看起来很简单,并且适用于大多数目的,但如果假设数据位于连续的内存块中,则会失败。
创建 2D 数组的另一种方法是使数据连续。
float** createFloat2D(int nRows, int nCols)
{
float** p1 = new float*[nRows]; // allocate row pointers
float* p2 = new float[nRows * nCols]; // allocate data in one chunk
for (int i = 0; i < nCols; ++i, p2 += nCols )
p1[i] = p2; // point the row pointers into the pool of memory
return p1;
}
void destroyFloat2D(float**f)
{
delete [] f[0];
delete [] f;
}
请注意,数据是在一个连续的"池"中创建的。 现在,使用yourArray[0]
实际上指向这个记忆的开始。 另请注意,销毁是在不必知道行数或列数的情况下完成的,因为 f[0] 指向内存池。
所以现在,这样的代码应该可以工作了
float** mGridB = createFloat2D(nThetaCells, nPhiCells);
//...
memcpy(mGridB[0], cur->mGridB[0], sizeof(float)*nThetaCells*nPhiCells);
如果我们使用创建 2d 数组的第二种方法,上面的代码现在可以正常工作。
我仍然会坚持使用 1-D 浮点数组的向量,因为您有指向数据的指针(请参阅我之前的评论(。 对于上面的代码,我会将其包装在一个可以轻松处理创建和销毁的类中。
最后一件事是复制构造函数。 C++ 中的复制构造函数具有以下可能的签名:
DGCPM(const DGCPM&);
DGCPM(DGCPM&);
DGCPM(volatile DBCPM&);
我可能错过了一个,但签名应该是上面的签名之一,很可能是第一个(您也可以在引用参数之后有其他参数,但它们都必须具有默认值(。
请注意,正如代码所述,DBCPM*
不是复制构造函数的有效参数 - 请记住,复制构造函数不仅供使用,而且编译器也将使用它来制作副本。 因此,要向编译器发出"是的,此函数用于制作副本"的信号,您的函数必须与上面的签名之一匹配。
此外,你需要一个赋值运算符,换句话说,类需要实现"3 法则"。
这听起来很愚蠢(基本编程错误(:我的索引"i"超出了(模型数量 - 1(,所以我在访问不存在的内存时遇到了分段错误。
- 为什么C++在将一个对象复制到另一个对象时需要对这两个对象进行低级常量限定
- 如何在向量列表初始化时避免对象复制以及如何延长临时的生存期
- 了解在 C++ 中分配容器时的对象复制
- C++类对象复制构造函数和运算符=
- 复制 elision/RVO 会导致从同一对象复制/移动吗?
- 如何在循环内将大对象复制到omp任务中?
- 为什么当我添加一个不同的对象(复制构造函数中的参数)时调用复制构造函数?
- 无法将类构造函数中新创建的对象复制到 C++ 中的向量成员
- 将C++对象复制到指向新指针的指针处
- 将一个 OpenCV OutputArrayOfArrays 对象复制到另一个对象
- 选择性地禁用隐式对象复制
- 不能从文件到指针向量的对象复制
- 从派生对象复制构造
- OpenCV Mat对象复制速度更快
- 将子对象复制到父对象
- 为什么普通的 c++ 编译器不优化对象复制?
- C :将一个对象复制到构造函数中的另一个对象
- 使用命名对象复制构造函数
- 不必要的对象复制 - C++ STL
- 如何将一个类的对象复制到另一个类中