对象在C++中作为参数传递给函数时被修改

Objects modified when they are passed to a function as arguments in C++

本文关键字:函数 修改 参数传递 C++ 对象      更新时间:2023-10-16

我有以下简单的向量类,其中我可以定义不同大小的向量并将它们求和;我重载了+运算符,并包含了一个简单的数据显示方法deslegar();该类定义为:

class Cvector{
   private:
   int dim;
   float * C;
public:
  Cvector(){};
  Cvector(int n){dim=n; C = new float[dim];};
  ~Cvector(){delete [] C;};
  int getdim(){return dim;};
  float getC(int i){
    if(i<dim && i>=0)
      return C[i];
    else
      return 0;
  };
  void setC(int i, float x){
    if(i<dim && i>=0)
        C[i]=x;
  }
  void desplegar(){
    cout<<"[ ";
    for(int i=0;i<dim;i++)
        cout<<getC(i)<<" ";
    cout<<" ]"<<endl;
  }
  Cvector operator + (Cvector);
};
Cvector Cvector::operator + (Cvector A){
    int n;
    if(A.getdim()>dim)
        n=A.getdim();
    else
        n=dim;
    Cvector temp(n);
    for(int i=0;i<n;i++)
        temp.setC(i,getC(i)+A.getC(i));
    return temp;
}

当我运行以下代码时,一切都很好:

Cvector X(2);
Cvector Y(3);

定义维度2和3 的对象

X.setC(0,1);
X.setC(1,4);
Y.setC(0,0);
Y.setC(1,6);
Y.setC(2,9);

将相应的值添加到每个维度(X为2,Y为3)

X.desplegar();
Y.desplegar();
Cvector T=Y+X;
T.desplegar();

所有的东西都显示正确,直到这里,T显示向量Y和向量X的和,这就是我想要的。但当我运行时

X.desplegar();

它只显示来自内存的无关数据,当对象X作为参数传递给运算符+时,它似乎被修改了,为什么会这样?如果我使用指向对象的指针,这不会发生,但发生这种情况似乎并不直观。

有人能评论吗?

当用参数pass-by-value重载operator +时,会调用复制构造函数来实例化临时对象。默认情况下,副本构造函数是原始对象的逐位副本。就在该方法返回之前,在临时对象上调用用户定义的析构函数,在该对象中释放底层存储。

若要更正代码,请自行定义复制构造函数或通过引用传递参数。

请永远记住三条规则,即当你定义了析构函数、复制构造函数和赋值运算符中的任何一个时,请考虑定义这三个函数中的其他函数。

问题出在operator+中。

Cvector Cvector::operator + (Cvector A)

在这里,您通过对第二个操作数(在您的情况下是X)进行复制来传递它。这个复制(A)将使它的C指针指向与X向量相同的内存:

Cvector T=Y+X;

operator+结束时,副本将被销毁,析构函数将被调用,内存将被释放。由于此内存由X的副本和X本身共享,因此您将拥有垃圾。更改您的operator+,使其成为参考,一切都会正常工作:

Cvector Cvector::operator + ( const Cvector & A )

请注意,实现复制构造函数也是一个好主意。

您复制矢量不正确temp运算符+结束时自动调用其析构函数,导致T有一个悬空指针。你可能正在做类似于X的事情,如果不看完整的程序,很难判断。

Cvector没有复制构造函数,因此将生成默认构造函数:

Cvector::Cvector(Cvector const & original)
  : dim(original.dim), C(original.C)
{}

这将执行复制,也就是说,您现在有两个具有相同C指针的对象。

因此,当您X作为参数复制到任何函数时,都会发生以下情况:

Cvector x;
// populate X
// now call this:
void foo(Cvector y) { // y is created as a copy of x
} // y is destroyed

当复制的参数被销毁时,它会删除与原始参数共享的C。


应进行以下更改:

  1. 适当编写一个副本构造函数和赋值运算符,用于执行深度复制(这将阻止副本在销毁时破坏原始对象)
  2. 在不更改参数时通过const引用传递参数