-1

When ever I run my program it breaks and has the error "A heap has been corrupted" when debugging the program, it actually will go through the whole thing just fine and break on the system("PAUSE") which seems like an odd place to have an error. I am clueless where the issue is. The program worked just fine, until I added the operator overload for + and OS

Below is my code:

MAIN.CPP

#include "stdafx.h"
#include "vector.h"

// the printV function
// used to test the copy constructor
// parameter: a MyVector object

void printV(Vector);


int main()
{
    cout << "\nCreating a vector Sam of size 4.";
    Vector sam(4);

    cout << "\nPush 12 values into the vector.";
    for (int i = 0; i < 12; i++)
        sam.push_back(i);

    cout << "\nHere is sam: ";
    cout << sam;
    cout << "\n---------------\n";

    cout << "\nCreating a vector Joe of size 4.";
    Vector joe(4);
    cout << "\nPush 6 values into the vector.";
    for (int i = 0; i < 6; i++)
        joe.push_back(i * 3);

    cout << "\nHere is joe: ";
    cout << joe;
    cout << "\n---------------\n";

    cout << "\nTest the overloaded assignment operator \"joe = sam\": ";
    joe = sam;

    cout << "\nHere is sam: ";
    cout << sam;
    cout << "\n---------------\n";

    cout << "\nHere is joe: ";
    cout << joe;
    cout << "\n---------------\n";

    // pass a copy of sam by value
    printV(sam);

    system("PAUSE");
    return 0;
}

void printV(Vector v)
{
    cout << "\n--------------------\n";
    cout << "Printing a copy of a vector\n";
    cout << v;
}

VECTOR.H

#pragma once
#include <iostream>
#include "stdafx.h"

using namespace std;

class Vector
{

private:
    int vectorSize;
    int vectorCapacity;
    int *vectorArray;

public:

    //A default constructor that creates an vector with a default capacity of 2
    Vector();

    //A parameterized constructor that creates a vector of capacity n
    Vector(int n);

    // A function, size(), that returns the size of your vector.
    int size();

    // A function, capacity(), that returns the capacity of the vector.
    int capacity();

    // A function, clear(), that deletes all of the elements from the vector and resets its size to zero and its capacity to two.
    void clear();

    // A function push_back(int n) that adds the integer value n to the end of the vector.If the vector is not large enough to hold this additional value, you must make the vector grow.Your grow algorithm should double the current capacity of the vector.Don't forget to consider the case where the initial capacity of the vector is zero.
    void push_back(int n);

    // A function at(int n) that returns the value of the element at position n in the vector.If the index n is greater than the size() of the vector, this function should throw an exception.
    int at(int n);

    friend ostream& operator<<(ostream& os, Vector vt);

    Vector operator=(Vector&);

VECTOR.CPP

#include "stdafx.h"
#include "vector.h"


Vector::Vector()
{
    vectorSize = 0;
    vectorCapacity = 0;
    vectorArray = 0;

}
// Create new array with given capacity
Vector::Vector(int n) 
{
    vectorCapacity = n;

    vectorArray = new int[vectorCapacity];

}
//Return array size
int Vector::size() 
{
    return vectorSize;
}

// Return array capacity
int Vector::capacity()
{
    return vectorCapacity;
}

// clear array values
void Vector::clear()
{
    for (int i = 0; i < sizeof(vectorArray); i++)
    {
        vectorArray[i] = '\0';
    }

    vectorSize = 0;
    vectorCapacity = 2;
}


// Add number to array and double array size if needed

void Vector::push_back(int n)
{
    int test = 100;
    if (vectorCapacity > vectorSize)
    {
        vectorArray[vectorSize] = n;
        vectorSize++;

    }
    else {

        if (vectorCapacity == 0) {
            vectorArray = new int[4];
            vectorArray[0] = n;
            vectorCapacity = 4;
            vectorSize++;
        }
        else {

            int newCapacity = vectorCapacity * 2;

            // Dynamically allocate a new array of integers what is somewhat larger than the existing array.An algorithm that is often used is to double the size of the array.

            int *tempArray = new int[newCapacity];

            // Change capacity to be the capacity of the new array.

            vectorCapacity = newCapacity;

            // Copy all of the numbers from the first array into the second, in sequence.

            for (int i = 0; i < Vector::size(); i++)
            {
                tempArray[i] = vectorArray[i];
            }

            delete[] vectorArray;
            vectorArray = new int[newCapacity];

            for (int i = 0; i < Vector::size(); i++)
            {
                vectorArray[i] = tempArray[i];
            }

            delete[] tempArray;

            // Add the new element at the next open slot in the new array.

            vectorArray[vectorSize] = n;

            // Increment the size;

            vectorSize++;

        }
    }
}


// Return Value and given point in array

int Vector::at(int n)
{
    return vectorArray[n];
}

// Cout Vector
ostream& operator<<(ostream& os, Vector vt)
{
    int size = vt.size();

    for (int i = 0; i < size; i++) {

        os << "index " << i << " is " << vt.at(i) << endl;

    }

    return os;
}

// Set one vector to equil another
Vector Vector::operator=(Vector& right) {

    // Clear array on left
    for (int i = 0; i < sizeof(vectorArray); i++)
    {
        vectorArray[i] = '\0';
    }

    vectorSize = right.size();
    vectorCapacity = right.size() * 2;

    // Assign values from left to right
    for (int i = 0; i < vectorSize; i++)
    {
        vectorArray[i] = right.at(i);
    }

    return vectorArray[0];

}
Brandon Turpy
  • 883
  • 1
  • 10
  • 32
  • 1
    I think it may be the operator overload function on Vector.cpp for = ... not not sure why. – Brandon Turpy Nov 15 '15 at 20:26
  • 1
    For one, `sizeof(vectorArray)` is a size of a pointer. Then you actually used the correct one: `vectorSize`. – LogicStuff Nov 15 '15 at 20:39
  • But the whole clearing shouldn't even be done? Shouldn't you reallocate the contents? There's also `return vectorArray[0]`, which will just implicitly convert to `Vector` calling `Vector(int n)` – LogicStuff Nov 15 '15 at 20:42

3 Answers3

2

The problem is operator=()

Why ?

You start with sam having a capacity of 4. You push back 12 items in it. When you reach the 5th element, the capacity is doubled from 4 to 8. Then you then reach the 9th element, the capacity is increased to 24.

You then have joe with an initial capacity of 4. You push back 6 items in it. When you reach the 5th element, its capacity is increased to 8.

When you then do joe = sam, your operator overwrites joe's size and capacity but without verifying that the capacity matches, and without allocating missing capacity. As you then try to copy 12 elements in a vector having in reality only a capacity of 8, you do some collateral damage in memory and corrupt the heap.

Solution

Do not overwrite blindly the capacity. Keep the capacity if it's sufficient. If not, align the capacity and reallocate.

// Set one vector to equal another
Vector Vector::operator=(Vector& right) {

    //...    

    if (vectorCapacity < right.vectorCapacity) {
        delete[] vectorArray;   // assuming pointer is either nullptr or valid array
        vectorArray = new int[right.vectorCapacity];
        vectorCapacity = right.vectorCapacity;   
    }
    vectorSize = right.size();
  
    // Assign values from left to right
    //...

    return *this;
}

Note that it would be better to return the vector by reference !

Community
  • 1
  • 1
Christophe
  • 68,716
  • 7
  • 72
  • 138
  • The `if` protecting the `delete[]` is pointless. If `vectorCapacity` and `vectorArray` were kept consistent (so either both are zero or both are nonzero) then the test isn't "wrong", just useless. But if a test there had any effect, that effect would be wrong. `delete[]` correctly handles null pointers. – JSF Nov 15 '15 at 20:59
  • I think the pattern of intent would say `this->vectorCapacity` should be computed from `right.size()` not from `right.vectorCapacity`. It is not seriously "wrong" to set it the way did, but is both inferior design and contrary to the intent shown by the rest of the code. – JSF Nov 15 '15 at 21:03
  • Thanks, I got it to work... now when I add a deconstructor on the class, it has an error when I call delete[] vectorArray; now, when I remove the deconstructor it is fine. Any ideas why that would be? – Brandon Turpy Nov 15 '15 at 21:15
  • @BrandonTurpy The proper term is "destructor", not "deconstructor". Second, removing the destructor does not fix the problem, it just hides it. The destructor is exposing the further problems you have with the copy semantics of your `Vector` class. More than likely, you failed to implement a copy constructor. – PaulMcKenzie Nov 15 '15 at 22:12
  • @JSF actually, the code doesn't guarantee that when capacity is 0 the array is `nullptr`. In fact constructing Vector ouch(0) might initialisze the array to a non null pointer despite a 0 capacity (see this [SO answer](http://stackoverflow.com/a/1087066/3723423)). This is why I protect the `delete[]` with `if` – Christophe Nov 15 '15 at 22:24
  • @JSF about capacity and size, I fully agree with your point. Personnally, I would use a private function to manage (re)allocation and capacity, that would be common to construction, copy construction and assignement. Nevertheless, the OP lets the user of his class control the capacity at construction. With this approach, the reuse of capacity for the copy seems semantically acceptable as well. – Christophe Nov 15 '15 at 22:32
  • @Christophe, that other answer you linked says "you can only pass it to array delete - and you should delete it." so it is an example of the "protection" you provided being **wrong** rather than worthless. You make a somewhat convincing case that the variables can be out of sync, so that your `if` does make a difference. But when it makes a difference, it makes a harmful difference. – JSF Nov 16 '15 at 16:47
  • @JSF well, i have to admit that you mark a point here... I'll correct it :-) – Christophe Nov 16 '15 at 18:07
1

There are lots of errors, but the one that causes the described symptoms is the operator= never allocates a new array of int for vectorArray

Each use of sizeof(vectorArray) is also wrong. That is just the size of a pointer, not the allocation of the area pointed to.

Each place that does vectorArray[i] = '\0'; is at best pointless, and whatever was intended, that is the wrong way to do it. Enough so I can't even guess the intent.

In the clear function the only necessary step was vectorSize = 0; The rest was at best pointless. Setting capacity to 2 there is bizarre, though it does no major harm.

operator= ought to have return type Vector& and not Vector and should return *this not construct a Vector whose capacity is a value from the old one. In general, almost any operator= member of a class should return *this. Exceptions to that rule are way beyond the level where you are currently trying to learn.

JSF
  • 5,281
  • 1
  • 13
  • 20
  • So in my head, this is what I have been thinking and I think what your saying, a lot of it I thought I did. From the begining I clear out the current vector on the left that we are going to assign the values on the right to. Then I change size of the left array to the size of the right array, change the capacity of the array on the left to 2 times the array on the right so it has room to be added to. Then I insert the values from the left to the right array. And it had an error that I had to return a Vector so I put the return in there even though I know it has nothing to do with it. – Brandon Turpy Nov 15 '15 at 20:49
0

Given all of the answers so far, the other issue is that you failed to implement a user-defined copy constructor:

Vector(const Vector& n);

This function has to be implemented correctly since you have functions returning Vector by value. Since you didn't implement it, copying will not work correctly.

Second issue is that you should be returning the Vector by reference, not by value in the operator= function.

My first suggestion is to take whatever code you have now in your operator= and do the work of the "real" copying in the copy constructor. Here is a streamlined version of what the copy constructor should look like:

#include <algorithm>
//..
Vector::Vector(const Vector& rhs) : vectorCapacity(rhs.vectorCapacity), 
                                    vectorArray(new int[rhs.vectorCapacity]), 
                                    vectorSize(rhs.size())
{
    std::copy(rhs.vectorArray, rhs.vectorArray + rhs.vectorCapacity, vectorArray);
}

Note the usage of the member initialization list, and the call to the function std::copy (you could also have written a loop, but just to show you that there are functions that do the copy without hand-written loops).

The second thing is that your destructor should simply do this:

Vector::~Vector()
{  delete [] vectorArray; }

Then operator= becomes very simple using copy / swap.

#include <algorithm>
//...
Vector& operator=(const Vector& v)
{
   Vector temp = v;
   swap(this.vectorCapacity, temp.vectorCapacity);
   swap(this.vectorArray, temp.vectorArray);
   swap(this.vectorSize, temp.vectorSize);
   return *this;
}

This works only if the copy constructor and destructor work correctly, since operator= takes advantage of these functions.

Read more about the Rule of 3 and the copy / swap idiom.

Community
  • 1
  • 1
PaulMcKenzie
  • 34,698
  • 4
  • 24
  • 45