C ++深层复制对象

时间:2014-04-14 18:47:22

标签: c++

我正在尝试来回深层复制对象。当我运行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;
}

4 个答案:

答案 0 :(得分:1)

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;
};

免费提供深层拷贝。免费删除内存。

免费我的意思是你不必编写代码..

答案 1 :(得分:0)

从您的课程定义中的评论/* [nPhiCells][nThetaCells] */,我认为您将float**视为2D数组。但是,如果您可以像二维数组一样使用它们,它们实际上是指针到数组的数组。这是一个巨大的差异:这意味着,您必须复制nPhiCellsnThetaCells元素的各个数组,您必须设置指针数组本身。现在,当你做

memcpy(mGridHalf[0], cur->mGridHalf[0],sizeof(float)*nThetaCells*nPhiCells);
在您的复制构造函数中,您假设没有指针数组,并且所有行数组在内存中是连续的。这个副本超出指针数组的边界(segfaulting),或通过mGridHalf[i][j]访问数组只是做错了,将float数据重新解释为指针(和segfaulting)。


不幸的是,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]可以避免自己进行索引计算。

答案 2 :(得分:0)

问题很可能是你假设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);

如果我们使用创建二维数组的第二种方法,上面的代码现在可以正常工作。

我仍然坚持使用1-d浮点数组的向量,因为你有指向数据的指针(参见我之前的评论)。对于上面的代码,我会将它包装在一个容易处理创建和破坏的类中。

最后一件事是复制构造函数。 C ++中的复制构造函数具有以下可能的签名:

DGCPM(const DGCPM&);
DGCPM(DGCPM&);
DGCPM(volatile DBCPM&);

我可能错过了一个,但签名应该是上面的一个,很可能是第一个(你可以在引用参数之后还有其他参数,但它们都必须有默认值)。

请注意,DBCPM*不是代码所述的复制构造函数的有效参数 - 请记住复制构造函数不仅可以使用,而且编译器也会使用它来复制。因此,要向编译器发出“是,此函数用于复制”的信号,您的函数必须与上述签名之一匹配。

此外,您需要一个赋值运算符,换句话说,该类需要实现“3规则”。

答案 3 :(得分:0)

这听起来很愚蠢(基本编程错误):我的索引“i”超出了(模型的数量 - 1),所以我从访问不存在的内存中得到了分段错误。