为什么我在 IntegerSet 类中收到堆损坏错误?

Why am I receiving a heap corruption error in IntegerSet class?

提问人:Joe Forsyth 提问时间:7/26/2023 最后编辑:Code_Sphinx_TSKJoe Forsyth 更新时间:7/28/2023 访问量:44

问:

我有一个作业,我必须创建一个名为 IntegerSet 的类。关键是它创建了整数集。如果集合中存在整数,则该数字在数组集中的位置为 1,如果没有,则为 0。我一直在研究动态内存分配,所以我认为我需要保留这部分。但是,当我运行该程序时,它允许我输入最多第二个数字。然后它会出现一个错误,关于堆内存损坏。我根本不知道如何解决它。我想也许我的某些方法中的内存范围可能会有点偏离。或者我的动态内存分配有其他问题。

由于在我的课堂上,我们正在研究动态内存分配、指针和按引用传递,我希望能够将这些方面保留在程序中,并学习如何使它们正常工作。

下面是 .h 文件:

#ifndef INTEGERSET_H
#define INTEGERSET_H

class IntegerSet
{
private:
    int *setArray;
    int setSize;

public:
    IntegerSet(int = 0);
    IntegerSet(const IntegerSet&);
    ~IntegerSet();

    IntegerSet* unionOfSets(const IntegerSet&) const;
    IntegerSet* intersectionOfSets(const IntegerSet&) const;

    bool insertElement(int);
    bool deleteElement(int);
    void printSet() const;

    bool isEqual(const IntegerSet&) const;

};

#endif

下面是 .cpp 文件:


#include "IntegerSet.h"
#include <iostream>
using namespace std;

//Constructor
IntegerSet::IntegerSet(int size)
{
    setArray = new int[size];
    setSize = size;

    for (int i = 0; i < size; ++i)
    {
        setArray[i] = 0; //Initializes the set to 0;
    }
}

//Copy constructor
IntegerSet::IntegerSet(const IntegerSet &setToCopy) 
    : setSize(setToCopy.setSize)
{
    setArray = new int[setSize];

    for (int i = 0; i < setSize; ++i)
    {
        setArray[i] = setToCopy.setArray[i];
    }
}

//Destructor
IntegerSet::~IntegerSet()
{
    delete[] setArray;
}

//Prints set
void IntegerSet::printSet() const
{
    for (int i = 0; i < setSize; ++i)
    {
        if(setArray[i] == 1)
            cout << i + 1 << " ";
    }
}

//Unites two sets into a new set
IntegerSet* IntegerSet::unionOfSets(const IntegerSet &otherSet) const
{
    IntegerSet* thirdSet = new IntegerSet; //Dynamically allocates a new set

    for (int i = 0; i < setSize; ++i)
    {
        if (this->setArray[i] == 1 || otherSet.setArray[i] == 1)//
        {
            thirdSet->insertElement(i); //Assigns the new set the united sets' values
        }
    }
    return thirdSet; //Returns pointer to new resulting set.
}

//Inyersects two sets to the new set
IntegerSet* IntegerSet::intersectionOfSets(const IntegerSet &otherSet) const
{
    IntegerSet* extraSet = new IntegerSet;

    for (int i = 0; i < setSize; ++i)
    {
        if (setArray[i] == 1 && otherSet.setArray[i] == 1)
        {
            extraSet->insertElement(i); //Assigns the new set the intersecting sets' values
        }
    }

    return extraSet; //Returns pointer to the new resulting set
}

//Inseerts new element
bool IntegerSet::insertElement(int k) 
{
    if (k <= setSize && k >= 0)
    {
        setArray[k] = 1;
        return true;
    }
    else
        return false;
    
}

//Deletes element
bool IntegerSet::deleteElement(int m)
{
    if (m <= setSize && m >= 0)
    {
        setArray[m] = 0;
        return true;
    }
    else
        return false;
    
}

//Checks if two sets are equal
bool IntegerSet::isEqual(const IntegerSet &otherSet) const
{
    for (int i = 0; i < setSize; ++i) //Access each element
    {
        if (setArray[i] != otherSet.setArray[i]) 
            return false; //If even one element is not equal then the sets cannot be equal
    }
    return true; //If after the loop finds none of the elements are equal, then the sets must be equal
}

下面是客户端测试文件:

#include "IntegerSet.h"
#include <iostream>
using namespace std;

int main()
{
    int input; //User input
    IntegerSet set1(3);
    IntegerSet set2(3);
    IntegerSet* set3 = nullptr;
    IntegerSet* set4 = nullptr;

    
    cout << "Enter a number between 1 and 3 to add to the first set: ";
    cin >> input;
    while (!set1.insertElement(input - 1))
    {
        cout << "\nThat is an invalid number. Please enter a number between 1 and 3: ";
        cin >> input;
    }
    set1.insertElement(input - 1);

    cout << "Enter a number between 1 and 3 to add to the first set: ";
    cin >> input;
    while (!set2.insertElement(input - 1))
    {
        cout << "\nThat is an invalid number. Please enter a number between 1 and 3: ";
        cin >> input;
    }
    set2.insertElement(input - 1);

    cout << "Set 1 is: ";
    set1.printSet();
    cout << "\nSet 2 is: ";
    set2.printSet();
    cout << endl; 

    set3 = set1.unionOfSets(set2);
    set4 = set1.intersectionOfSets(set2);

    cout << "The union of Set 1 and Set 2: " << endl;
    cout << "Set 3 - {";    // I tried using this first: 
    set3->printSet();       //cout << "Set 1 - {" << set1.printSet() << "}" << endl;
    cout << "}" << endl;    // But it wouldn't work because the second << was being show as an ambiguity error

    cout << "The intersection of Set 1 and Set 2: " << endl;
    cout << "Set 4 - {";    
    set4->printSet();           //Same formatting reasons as above.
    cout << "}" << endl;
    
    if (set1.isEqual(set2))
        cout << "Set 1 and Set 2 are equal" << endl;
    else
        cout << "Set 1 and Set 2 are not equal" << endl;

    set3->deleteElement(1);
    set4->deleteElement(1);

    cout << "After removing 2 from Set 3 and Set 4" << endl
        << "Set 3 is now - {";
    set3->printSet();
    cout << "}" << endl;
    cout << "Set 4 is now - {";
    set4->printSet();
    cout << "}" << endl;

    delete set3;
    delete set4;

    system("PAUSE");
    return 0;
}

该程序应该要求用户提供两个整数,只是为了测试。然后,它将做一些他们创建的集合的并集和交集的示例。最终发生的事情是,它会到达第二个输入,然后出现堆损坏错误,并且显示联合集的字符串不会打印。

C 可视化 C++ 按引用 dynamic-memory-allocation 按指针传递

评论

1赞 Alan Birtles 7/26/2023
k <= setSize应该是k < setSize
0赞 Jesper Juhl 7/26/2023
什么是调试器,它如何帮助我诊断问题?
0赞 john 7/26/2023
您正在学习如何使用动态分配和指针。因此,在类的内部使用指针是可以的。但是,如果您的类,则根本没有理由在接口中或界面中使用指针。这是一个完全不同的情况。一定要使用指针,但要将它们隐藏在类中。所以改为 和 类似地 ,现在不需要指针了。IntegerSetmainIntegerSetIntegerSet* IntegerSet::unionOfSets(const IntegerSet &otherSet) constIntegerSet IntegerSet::unionOfSets(const IntegerSet &otherSet) constintersectionOfSetsmain
0赞 Joe Forsyth 7/26/2023
谢谢!我按照您的建议做了,这已经阻止了堆损坏错误。但是,set3 和 set4 仍未打印。你能如何补救吗?

答:

4赞 Some programmer dude 7/26/2023 #1

一个主要问题是在函数中,你创建了一个新集合,如下所示:unionOfSets

IntegerSet* thirdSet = new IntegerSet;

这意味着默认设置大小将为。这意味着您在构造函数中创建的数组的大小为零。

然后调用时,您可以检查索引是否小于或等于设置的大小。这是真的,因为这是真的。insertElement0 <= 0

因此,您将取消引用零大小数组的第一个元素。导致未定义的行为和可能的崩溃。

您需要为您创建的新集设置特定大小。并更新签入。insertElement


我还建议你停止使用指针和动态分配,就像你的代码一样。如果您正确地实现了 3 或 5 的规则,那么您可以毫无问题地复制和移动集合。

此外,除非它是作业或练习的一部分,否则请使用而不是您自己的数组处理,然后您可以改用规则。std::vector<int>

评论

0赞 Joe Forsyth 7/26/2023
感谢您的帮助。我怎样才能不将其设置为默认零?此外,根据作业,我必须使用动态内存分配,这是本章的重点。该赋值还要求我们使用自己的数组处理,因为我们还没有掌握向量。
0赞 Some programmer dude 7/26/2023
@JoeForsyth ?new IntegerSet(setSize)
0赞 Joe Forsyth 7/26/2023
对不起,我只是迷失了方向,忘记了我可以这样做,我已经做了几个小时了哈哈。谢谢你,所以我按照你的建议做了,现在它完全按预期工作。非常感谢您的帮助!你太棒了!