内存释放对象的问题

时间:2011-09-21 17:47:21

标签: c++ c++11

我有一个班级

class vlarray {
public:
    double *p;
    int size;

    vlarray(int n) {
        p = new double[n];
        size = n;
        for(int i = 0; i < n; i++)
            p[i] = 0.01*i;
    }

    ~vlarray() {
        cout << "destruction" << endl;
        delete [] p;
        size = 0;
    }
};

当我在主

中使用时
int main() {
    vlarray a(3);
    {
        vlarray b(3);
        b.p[0] = 10;
        for(int i = 0; i < 3; i++) {
            cout << *(b.p+i) << endl;
        }
        a = b;
    }    // the magic happens here deallocation of b
    for(int i = 0; i < 3; i++) {
        cout << *(a.p+i) << endl;
    return 0;
}

当b解除分配的smth发生时..问题是什么,为什么会出现这个问题以及如何避免这类问题?

7 个答案:

答案 0 :(得分:5)

在这个问题的现有答案中似乎有些混淆,所以我会跳进去。


即时问题

您的主要问题是您尚未定义自己的副本分配运算符。相反,编译器会为您生成一个天真的编译器,因此当您运行a = b时,b内的指针将被复制到a中。然后,当b死亡并且其析构函数运行时,a内的指针不再有效。另外,a的原始指针已被泄露。

您自己的副本赋值运算符需要delete现有数组,分配一个新数组并复制您正在复制的对象中的内容。

更一般地

更进一步,您还需要定义一些其他内容。这个要求巧妙地总结为Rule of Three (C++03)Rule of Five (C++11),并且在your favourite, peer-recommended C++ book网上有很多解释可以教你如何满足它。

或者,而不是......

更好的是,您可以开始使用std::vector而不是手动分配所有内容,并避免整个混乱:

struct vlarray {
    std::vector<double> p;

    vlarray(int n) {
        p.resize(n);
        for(int i = 0; i < n; i++)
            p[i] = 0.01*i;
    }
};

答案 1 :(得分:4)

您需要遵循C ++ 03中的 Rule of Three 和C ++ 11中的 Rule of Five

这些规则的背景和基础:

每当您的类具有带动态内存分配的指针成员时,以及使用任何复制函数(复制构造函数和复制赋值运算符在c ++ 03 中除非你重载这两个成员指针的深层拷贝,否则新创建的对象将继续指向父对象的内存分配(浅拷贝)。当父对象被销毁时出现问题(例如:通过超出范围),它的析构函数被调用,这通常会释放分配给指针成员的内存,当发生这种情况时,具有此指针的浅副本的对象现在指向无效的内存区域,并成为悬空指针。访问这些悬空指针会导致未定义的行为,并且很可能会崩溃。

为了避免这种情况,你需要遵循C ++ 03中的三条规则和C ++ 11中的五条规则 C ++ 03和C ++ 11中规则的区别,因为控制类的复制行为的函数在C ++ 11中已经改变。

三条规则基本上说明:
为您的班级实施复制构造函数复制赋值运算符析构函数

编辑:
如果您使用的是C ++ 11,那么三法则实际上变为 Rule of Five

答案 2 :(得分:0)

您未实施的内容称为:

简而言之,

  

三规则(也称为三巨头或三巨头的规则)是C ++中的经验法则,声称如果一个类定义了下面的一个,它应该明确地定义所有三个:

     
      
  1. 析构者
  2.   
  3. 复制构造函数
  4.   
  5. 复制分配操作员
  6.   

由于您也标记了问题C++11,因此您必须实现此目的:

答案 3 :(得分:0)

我认为问题在于你没有复制构造函数。编译器试图尽力而为,但并不总是成功。编写一个复制构造函数,然后重试。

答案 4 :(得分:0)

b的销毁导致成员指针p被删除。由于此指针已复制到a,因此a的成员指针指向已删除的内存。你有不确定的行为。

要避免这种情况,您需要在将一个对象复制到另一个对象的任何位置执行deep copy,通常是复制构造函数和赋值运算符。分配一个新数组并将所有元素从一个数组复制到另一个数组。您现在遵守Rule Of Three,因为您已经定义了析构函数。

解决此问题的最佳方法是完全避免使用原始指针,并将其替换为std::vector

答案 5 :(得分:-1)

实例之间的直接转移共享P。创建一个复制构造函数。

答案 6 :(得分:-1)

将b分配给(a = b)时,编译器生成的复制构造函数会对数据成员执行浅层复制。我并不尊重这些指针。因此,它们共享相同的资源,当它们中的任何一个被销毁时,它们就会将它们的共享资源与它们一起使用

定义您自己的复制构造函数以执行深层复制,或使用像vector这样的数组抽象。

class vlarray {
public:
  std::vector<double> p;
  int size;
  vlarray(int n) {
    p.resize(n);
    size = n;
    for(int i = 0; i < n; i++) p[i] = 0.01*i;
  }
  ~vlarray() {
    cout << "destruction" << endl;
    }
};