【问题标题】:operator overloading memory leak运算符重载内存泄漏
【发布时间】:2025-12-24 19:55:07
【问题描述】:

最近我有一个任务要在 C++ 中完成,实现一个带有联合、交集等作为重载运算符的 Set 类。我遇到了重载运算符+()的问题。我决定使用向量并利用一些算法的库函数。问题是我必须将数组指针和数组大小传递给构造函数。这使这个任务有点复杂......我可以编译它,但是在“z = a + b”操作期间我遇到了某种内存泄漏。谁能解释我做错了什么?

class Set {
    int number; // array size (can't be changed)
    int *elems; // array pointer (same)

  public:

    Set();
    Set(int, int*); // (can't be changed)
    ~Set();

  friend Set operator+(const Set& X,const Set& Y){
    std::vector<int> v(X.number+Y.number);
    std::vector<int>::iterator it;

    it=std::set_union (X.elems, X.elems+X.number, Y.elems, Y.elems+Y.number, v.begin());
    v.resize(it-v.begin());

    Set Z;
    Z.number=v.size();
    Z.elems=&v[0];  

    return Z;
  }
};

Set::Set(){};
Set::Set(int n, int* array){
    number=n; 
    elems = array = new int[number];

    for(int i=0; i<number; i++) // creating Set
        std::cin >> elems[i];
    std::sort(elems, elems + number);
}

Set::~Set(){
    delete[] elems;
}

int main(){

   int* pointer;
   Set z;
   Set a = Set(5, pointer);
   Set b = Set(2, pointer);
   z=a+b;
}

我添加了复制构造函数和复制分配,按照 NathanOliver 的建议更改了 operator+(),现在我将传递给构造函数静态数组。仍然有内存泄漏,奇怪的是我得到了这个内存泄漏,即使在 main 中只有类变量初始化,是否有参数都没关系......有什么建议吗?我认为 cunstructor 是有效的。

Set::Set(int n, int* array){
   number = n; 
   elems = array;
   std::sort(elems, elems + number);
}

Set::Set(const Set& s){
   number=s.number;
   elems=s.elems;
}
Set& operator=(const Set& X){

   if(this==&X)
     return *this;
   delete [] elems;
   elems=X.elems;
   number=X.number;
   return *this;

我使用 gcc (tdm64-2) 4.8.1 编译器。

【问题讨论】:

  • 你需要定义一个赋值运算符。
  • Set 构造函数不应该创建elems 来自 array吗?你为什么读std::cin
  • 构造函数中指针的作用是什么?您只分配本地值,从不读取传入的内容。
  • @Barry,Kevin,如果我将传递给构造函数填充数组,数字变量的用途是什么?对我来说,这有点不合逻辑,但这些都是任务条件。或者也许我错过了那种方法?
  • @Saris 所以你知道数组中有多少东西。你不能仅仅从一个指针中得到它。

标签: c++ vector operator-overloading


【解决方案1】:

  friend Set operator+(const Set& X,const Set& Y){
    std::vector<int> v(X.number+Y.number);
    std::vector<int>::iterator it;

    it=std::set_union (X.elems, X.elems+X.number, Y.elems, Y.elems+Y.number, v.begin());
    v.resize(it-v.begin());

    Set Z;
    Z.number=v.size();
    Z.elems=&v[0];  

    return Z;
  }

您创建一个向量,对其进行修改,然后将elems 设置为指向向量包含的内容。这样做的问题是,当向量在函数结束时被销毁时,向量持有的内存被释放。所以你现在有一个指向你不再拥有的内存的指针。试图用它做任何事情都是未定义的行为。您可以做的是创建一个新数组,将vector 的元素复制到数组中,然后将新数组分配给`elems

Set Z;
Z.number= v.size();
Z.elems= new int[z.number];
for (int i = 0; i < Z.number; i++)
    Z.elems[i] = v[i];

return Z;

其次,您需要为您的类定义一个复制构造函数和赋值运算符。参考:What is The Rule of Three?

【讨论】:

    【解决方案2】:

    最好的解决方案(但我不知道是否允许您专门这样做)是在您的 Set 内部使用 vector 并使用两个迭代器从传入的指针和长度分配它构造函数。

    现在,如果这不可能,您需要正确管理班级的内存:

    • 您需要一个拷贝构造函数和拷贝赋值运算符。
    • 在您的operator+ 中,您不能创建一个本地向量然后获取其内存地址,一旦运算符返回,该内存就会消失。
    • 可能还有其他我没有发现的东西。

    【讨论】:

      【解决方案3】:

      当您有z=a+b 时,将使用Set 类的赋值运算符。您没有定义此运算符的自定义版本,因此使用默认的 compiler-generated 版本。这个编译器生成的赋值 operator=() 只是做了一个member-wise copy

      由于您的Set 类中有原始拥有指针,因此无法正常工作:默认编译器生成的operator=() shallow-copies 指针,相反,您应该深度复制数据。

      解决此问题的一种方法是定义您自己的operator=() 版本,注意对源数据进行正确的深度复制

      请注意,在这种情况下,您还应该定义一个复制构造函数

      但更好的选择是摆脱拥有的原始指针数据成员,而是使用 RAII 构建块 类,例如 std::vector

      因此,例如,而不是这些数据成员:

      int number; // array size (can't be changed)
      int *elems; // array pointer (same)
      

      你可以只有一个:

      std::vector<int> elems;
      

      如果你这样做,默认编译器生成的operator=() 会正常工作,因为它会复制std::vector 数据成员(不是原始的拥有指针),并且std::vector 知道如何正确复制其内容不会泄露资源。

      【讨论】: