按值返回的内存管理差异

Memory management differences in return by value

本文关键字:管理 内存 返回      更新时间:2023-10-16

我在这里学习一个教程:关于重载运算符,我发现了一些让我很困惑的东西。

在这个讨论本教程的网站上,之前有一个问题,即关于如何保留类中的变量,因为整个类都是按值传递的。

在尝试类定义时,我曾尝试过如下制作整数变量指针(也许不明智,但只是为了实验!(:

class CVector {  
  int* x;
  int* y;
  public:
  CVector () {};
  CVector (int,int);
  CVector operator + (CVector);
  ~CVector ();
};

在类构造函数中,我为两个整数分配内存,在类解构器中,我删除分配的内存。

我还调整了过载操作员功能如下:

CVector CVector::operator+ (CVector param) {
  CVector temp;
  *temp.x = *x + *param.x;
  *temp.y = *y + *param.y;
  return (temp);   
}

对于原始代码,如果类具有简单的整数变量,则整个类的按值返回成功完成。

然而,在我将变量更改为int指针后,由于整数变量不再完整,类的按值返回不会成功完成。

我假设当临时CVector超出范围并删除这些成员整数指针时,会调用解构器,但类本身仍然是按值返回的。

我希望能够在分配给其成员变量的内存完好无损的情况下按值返回CVector,同时确保临时CVector在超出范围时被正确删除。

有什么办法可以做到这一点吗?

非常感谢!

问题是你没有遵循这三者的规则,这基本上可以归结为:*如果你管理资源,那么你应该为你的类*提供复制构造函数赋值运算符析构函数

假设在构造时为指针分配内存,问题是隐式复制构造函数是,并且将复制指针,但您可能想要副本。在少数不需要深度副本的情况下,管理共享资源的控制会变得更加复杂,我会使用shared_ptr,而不是尝试手动执行。

您需要为CVector提供一个复制构造函数来复制分配的内存。否则,当您按值返回时,指针值将被简单地复制,然后temp对象将被销毁,从而释放int。返回的副本现在指向无效内存。

CVector( const CVector& other )
: x ( new int(other.x) )
, y ( new int(other.y) )
{}

请注意,在类中使用原始指针是个坏主意,尤其是多个。如果y的分配在上面失败,并且new抛出,那么就出现了内存泄漏(因为x处于悬空状态(。您可以在构造函数本身中进行分配,而不是在初始值设定项列表中,或者在try-catch中,或者使用newstd::nothrow版本,然后检查nullptr。但是它使得代码非常冗长并且容易出错。

最好的解决方案是使用一些智能指针类,如std::unique_ptr来保存指针。如果要使用std::shared_ptr来保存这些指针,甚至可以在类的副本之间共享int。

按值返回会将返回的temp对象复制到另一个对象,即临时"返回对象"。复制temp后,它将被销毁,从而释放您的int。处理此问题的最简单方法是使用引用计数指针,如tr1::shared_ptr<>。它将保留分配的内存,直到删除对它的最后一个引用,然后它将解除分配。

给定的代码中几乎没有问题。

(1( 您应该在构造函数中为*x*y分配适当的内存;否则访问它们是未定义的行为。

CVector () : x(new int), y(new int) {}

还要确保在重新分配它们之前,在delete xdelete y的位置有复制构造函数和operator =;否则会导致危险。

(2( delete在析构函数中

~CVector () { delete x; delete y; }

(3( 通过const引用将参数传递给operator +,以避免不必要的复制。

CVector CVector::operator+ (const CVector &param)
{
  // code
}

由于您正在学习指针,我不会对class的设计视角发表评论,比如它们应该是指针、变量还是容器等等。