繁体   English   中英

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子例程,该例程在程序中设置值。)

这是run.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数组一样使用它们,则它们实际上是指向数组的指针的数组。 那是一个巨大的差异:这意味着,您必须复制nPhiCellsnThetaCells元素的单个数组, 并且必须设置指针数组本身。 现在,当你做

memcpy(mGridHalf[0], cur->mGridHalf[0],sizeof(float)*nThetaCells*nPhiCells);

在副本构造函数中,您假定没有指针数组,并且所有行数组在内存中都是顺序的。 该副本超出了指针数组的范围(segfaulting),或者通过mGridHalf[i][j]访问数组只是做错mGridHalf[i][j] ,将float数据重新解释为指针(和segfaulting)。


不幸的是,C ++是与fortran多维数组进行交互的可怕语言,因为它没有可变大小数组的概念。 因此,以下是C代码,而不是C ++代码。 在C中,您可以解决以下问题:

float (*mGridHalf)[nThetaCells] = malloc(nPhiCells*sizeof(*mGridHalf));

将正确分配并键入可以使用以下命令访问的2D数组(即arrays的数组

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),所以我从访问不存在的内存中遇到了分段错误。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM