【问题标题】:Memory management differences in return by value按值返回的内存管理差异
【发布时间】:2011-07-23 20:38:13
【问题描述】:

我正在尝试学习有关重载运算符的教程 here:,但我发现了一些让我非常困惑的东西。

在这个网站here 上讨论了本教程,即关于如何保留类中的变量,因为整个类是按值传回的。

在尝试类定义时,我玩弄了使整数变量指针(也许不明智 - 但只是为了试验!)如下:

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 在超出范围时被正确删除。

有什么办法可以做到吗?

非常感谢!

【问题讨论】:

  • 你想用这些指针做什么?
  • 没什么特别的,我只是用它们替换了普通整数作为实验。

标签: c++ oop reference


【解决方案1】:

问题是你没有关注rule of the three,这基本上归结为:*如果你管理资源,那么你应该提供复制构造函数赋值运算符析构函数 用于您的班级*。

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

【讨论】:

    【解决方案2】:

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

    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 来保存这些指针,您甚至可以在类的副本之间共享整数。

    【讨论】:

    • 谢谢。关于您对原始指针的评论 - 我没有意识到可以在构造函数中声明变量?
    • 您不是在构造函数中声明它们,而是分配内存并将此内存分配给类定义中声明的指针。
    • 另外看看@David Rodríguez 的回答,你也应该为 CVector 提供一个赋值运算符。
    【解决方案3】:

    按值返回导致返回的temp 对象复制到另一个对象,即临时“返回对象”。 temp 被复制后,它被破坏,释放你的整数。处理此问题的最简单方法是使用引用计数指针,例如tr1::shared_ptr<>。它将保持分配的内存,直到删除对它的最后一个引用,然后它会释放。

    【讨论】:

      【解决方案4】:

      给定的代码很少有问题。

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

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

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

      (2) delete 它们在析构函数中

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

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

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

      由于您正在学习使用指针,因此我不会评论您的class 的设计观点,例如它们应该是指针还是变量还是容器等等。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-09-28
        • 1970-01-01
        • 2012-06-15
        • 2011-05-30
        • 2011-07-20
        相关资源
        最近更新 更多