1

I have an assignment in which I have to create a class called IntegerSet. The point is that it creates sets of integers. If an integer is present in the set, then that number has a 1 in its place in the array set, and a 0 if not. I have been studying dynamic memory allocation and so I think I need that part to stay. However, when I run the program, it allows me to input up to the second number. It then comes up with an error, something about heap memory corruption. I don't know how to fix it at all. I guess that maybe the memory scope in some of my methods might be off a bit. Or some other issue with my dynamic memory allocation.

Since in my class, we are studying dynamic memory allocation, pointers, and pass-by-reference, I would like to be able to keep those aspects in the program and learn how to make them work correctly.

Here is the .h file:

#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

Here is the .cpp file:


#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
}

And here is the client test file:

#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;
}

The program is supposed to ask the user for two integers, just to test. It would then do some examples of unions and intersections of the sets they create. What ended up happening was that it would get to the second input and then come up with a heap corruption error, and the strings showing the union sets do not print.

  • 1
    `k <= setSize` should be `k < setSize` – Alan Birtles Jul 26 '23 at 08:34
  • [What is a debugger and how can it help me diagnose problems?](https://stackoverflow.com/questions/25385173/what-is-a-debugger-and-how-can-it-help-me-diagnose-problems) – Jesper Juhl Jul 26 '23 at 09:01
  • You are learning how to use dynamic allocation and pointers. So using pointers in the internals of you `IntegerSet` class is fine. However there is no reason at all to use pointers in `main` or in the *interface* if your `IntegerSet` class. That's a completely different situation. Use pointers by all means but **hide** them inside your classes. So change `IntegerSet* IntegerSet::unionOfSets(const IntegerSet &otherSet) const` to `IntegerSet IntegerSet::unionOfSets(const IntegerSet &otherSet) const` and similarly for `intersectionOfSets`, now there is no need for pointers in `main`. – john Jul 26 '23 at 09:03
  • Thank you! I did as you suggested, which has stopped the heap corruption error. However, set3 and set4 are still not printing. Do you how I can remedy that? – Joe Forsyth Jul 26 '23 at 09:13

1 Answers1

4

A major problem is in your unionOfSets function where you create a new set like this:

IntegerSet* thirdSet = new IntegerSet;

That means the default set size will be zero. That means the array you create in the constructor will have zero size.

When you then call insertElement you have a check if the index is less than or equal to the set size. Which is true, because 0 <= 0 is true.

So you will then dereference the first element of your zero-size array. Leading to undefined behavior and likely crashes.

You need to set a specific size for the new set you create. And update the check in insertElement.


I also recommend you stop using pointers and dynamic allocation as much as your code it doing. If you implement the rule of three or five properly then you can copy and move the sets without any problem.

Also, unless it's part of the assignment or exercise, use std::vector<int> instead of your own array handling, and then you can follow the rule of zero instead.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
  • Thanks for your help. How can I make it not be set to the default zero? Also, per the assignment, I have to be using dynamic memory allocation, it's the focus of the chapter. The assignment also requires us to use our own array handling since we have not gotten to vectors yet. – Joe Forsyth Jul 26 '23 at 09:03
  • @JoeForsyth `new IntegerSet(setSize)`? – Some programmer dude Jul 26 '23 at 09:10
  • Sorry, I just lost track and forgot I could do that, I've been on this for hours haha. Thank you so so so so so much! I did as you suggested and now it's completely working as expected. Thank you so much for your help! You're awesome! – Joe Forsyth Jul 26 '23 at 09:27