1

I'm a beginner when it comes to C++ and have recently ran in to a very frustrating problem with my small program where I'm practicing operator overloading and templates.

I've created a template-class called SortedVector that can store instances of various types.

using namespace std;

template <class T, int size> class SortedVector {
public:
    SortedVector();
    bool add(const T& v);
    T& median();
    void sortArray();
    void removeLarge(const T& v);
    void print(ostream &os);
    void compexch(T& x, T& y);
    void sortArray(T* data, int s);

private:
    T arr[size];
    int arraySize;
};

template <class T, int size> SortedVector<T, size>::SortedVector() {


    arraySize = 0;

    for (int i = 0; i < size; i++) {
        arr[i] = T();
    }
}

template <class T, int size> bool SortedVector<T, size>::add(const T& v) {

    if (arraySize > size - 1) {
        cout << "Array is full!" << endl;
        return false;
    } else {

        arr[arraySize] = v;
        arraySize++;

        sortArray(arr, arraySize);
    }

    return true;
}

template <class T, int size> void SortedVector<T, size>::sortArray(T* data, int s) {
    for (int i = 0; i < s - 1; i++) {
        for (int j = i + 1; j < s; j++) {
            compexch(data[i], data[j]);
        }
    }
}

template <class T, int size > T & SortedVector<T, size>::median() {

}

template <class T, int size> void SortedVector<T, size>::removeLarge(const T & v) {

}

template <class T, int size> void SortedVector<T, size>::print(ostream & os) {


    for (int i = 0; i < arraySize; i++) {
        cout << arr[i] << endl;
    }
}

template <class T, int size> inline void SortedVector<T, size>::compexch(T& x, T& y) {
    if (y < x) {
        T temp = x;
        x = y;
        y = temp;
    }
}

It can store ints succesfully and it can also store Polygons (a custom made class created in a earlier assignment).

Polygon.h:

class Polygon {
public:

    Polygon(Vertex vertexArray[], int size);
    Polygon() : vertices(0), arraySize(0) {}
    ~Polygon() {delete[] vertices;}
    void add(Vertex v);
    float area();
    int minx();
    int maxx();
    int miny();
    int maxy();
    int numVertices() const {return arraySize;}
    friend ostream &operator << (ostream &output, const Polygon& polygon);
    friend bool operator > (Polygon polygon1, Polygon polygon2);
    friend bool operator < (Polygon polygon1, Polygon polygon2);

private:

    int arraySize;
    Vertex * vertices;

};

Polygon.cpp declaration:

using namespace std;

void Polygon::add(Vertex v) {
    arraySize++;
    Vertex * tempVertexes = new Vertex[arraySize];
    for (int i = 0; i < arraySize; i++) {
        if (i == arraySize - 1) {
            tempVertexes[i] = v;
        } else {
            tempVertexes[i] = vertices[i];
        }
    }
    delete [] vertices;
    vertices = tempVertexes;

}

Polygon::Polygon(Vertex vertexArray[], int size) {
    arraySize = size;
    vertices = new Vertex[size];

    for (int i = 0; i < size; i++) {
        vertices[i] = vertexArray[i];
    }
}

float Polygon::area() {
    float area = 0.0f;

    for (int i = 0; i < arraySize - 1; ++i) {
        area += (vertices[i].getXposition() * vertices[i + 1].getYposition()) - (vertices[i + 1].getXposition() * vertices[i].getYposition());
    }

    area += (vertices[0].getYposition() * vertices[arraySize - 1].getXposition()) - (vertices[arraySize - 1].getYposition() * vertices[0].getXposition());

    area = abs(area) *0.5;

    return area;
}



ostream& operator<<(ostream &output, const Polygon& polygon) { //Kolla denna!


    output << "{";
    for (int i = 0; i < polygon.numVertices(); i++) {
        output << "(" << polygon.vertices[i].getXposition() << "," << polygon.vertices[i].getYposition() << ")";

    }
    output << "}";

    return output;

}

bool operator>(Polygon polygon1, Polygon polygon2) {
    if (polygon1.area() > polygon2.area()) {
        return true;
    } else {
        return false;
    }
}

bool operator<(Polygon polygon1, Polygon polygon2) {
    if (polygon1.area() < polygon2.area()) {
        return true;
    } else {
        return false;
    }
}


template <class T> inline void compexch(T& x, T& y) {
    if (y < x) {
        T temp = x;
        x = y;
        y = temp;
    }
}

The code for the Vertex class:

class Vertex {
public:
    Vertex() : y(0), x(0) {}
    Vertex(int xPosition, int yPosition) : x(xPosition), y(yPosition) {}
    ~Vertex() {}
    int getXposition() const {return x;}
    int getYposition() const {return y;}

private:
    int x;
    int y;


};

The problem however is that the overloaded <<-operator seems print out the wrong values from the main-method:

int main() {

    SortedVector<Polygon, 10> polygons;
    SortedVector<int, 6> ints;

    ints.add(3);
    ints.add(1);
    ints.add(6);

    Vertex varr[10];
    varr[0] = Vertex(0, 0);
    varr[1] = Vertex(10, 0);
    varr[2] = Vertex(5, 2);
    varr[3] = Vertex(5, 5);
    polygons.add(Polygon(varr, 4));
    cout << "varr area:" << (Polygon(varr, 4)).area() << endl;

    varr[0] = Vertex(0, 0);
    varr[1] = Vertex(25, 8);
    varr[2] = Vertex(10, 23);
    polygons.add(Polygon(varr, 3));
    cout << "var area (1):" << (Polygon(varr, 3)).area() << endl;
    varr[0] = Vertex(0, 0);
    varr[1] = Vertex(5, 0);
    varr[2] = Vertex(5, 3);
    varr[3] = Vertex(4, 8);
    varr[4] = Vertex(2, 10);
    polygons.add(Polygon(varr, 5));
    cout << "var area (2):" << (Polygon(varr, 5)).area() << endl;
    polygons.print(cout);
    ints.print(cout);

    cout << "MEDIAN: " << ints.median() << endl;
    cout << "MEDIAN: " << polygons.median() << endl;

    return 0;

}

The code that is printed is:

var area (1):247.5
var area (2):33.5
{(6029504,0)(5,0)(5,3)}
{(6029504,0)(5,0)(5,3)(4,8)}
{(6029504,0)(5,0)(5,3)(4,8)(2,10)}
1
3
6
MEDIAN: 1
MEDIAN: {(6029504,0)(5,0)(5,3)}

Firstly, the method prints out the same polygon but with varying sizes. Secondly, it points out the wrong getXPosition() for the first object in the array. Everything else (that is implemented, like the ints and the area) is correct tho. Why is this? Am I missing something important here or am I just completely of with my program?

If theres any more code needed I am happy to provide it.

Regards

Tjernquist1
  • 124
  • 8
  • `SortedVector` default constructs `size` objects of type `T` even though there should not be any. `arr[i] = T();` inside the constructor does essentially nothing because `arr[i]` already contains a default constructed `T`. – nwp Feb 15 '16 at 22:18
  • 4
    *"I'm a beginner when it comes to C+"* -- Next time, use `std::vector` instead of attempting to write a dynamic array class. – PaulMcKenzie Feb 15 '16 at 22:21
  • 2
    I would if I could, however this is part of a larger assignment where we, as student, have to write classes that provide the functionality that std::vector provides (I assume this is to provide us with better understanding of the classes) – Tjernquist1 Feb 15 '16 at 22:22
  • @Tjernquist1 Then if that's the case, write a dynamic array class and only a dynamic array class. Test it thoroughly, and once it's tested with simple data, *then* use it in a larger program. We don't know if the bugs are in your dynamic array class or not, in other words, you could have a shaky foundation. – PaulMcKenzie Feb 15 '16 at 22:25
  • nwp, oh I see, I was under the impression that I had to instantiate the T objects with T(); when working with templates. How else would I construct the neccessary space needed to house the objects that are to be entered in the SortedVector? – Tjernquist1 Feb 15 '16 at 22:27
  • 1
    Unfortunately, the pieces of code you provide don't allow to reproduce your problem, so the chances of getting help are slim: we can at best guess what went wrong in the unknown part of the code. – Gassa Feb 15 '16 at 22:27
  • I will provide the full code, give me 1 sec! – Tjernquist1 Feb 15 '16 at 22:27
  • 1
    1. You can try and minimize the code while keeping the problem (http://sscce.org/ might help) - and more often than not, the problem will become obvious in the process already. – Gassa Feb 15 '16 at 22:27
  • 1
    2. Alternatively, you may choose to upload your whole code to some public repository and link it from here. – Gassa Feb 15 '16 at 22:28
  • 1
    Your problem appears to have nothing to do with `operator <<`. – user253751 Feb 15 '16 at 22:29
  • 1
    Also, your `Polygon` class, given what you posted, is seriously flawed as it does not have a user-defined copy constructor and assignment operator, along with a destructor. Without those functions, your entire program is infested with memory leaks. Search for "The Rule of 3" here on SO. – PaulMcKenzie Feb 15 '16 at 22:29
  • @DanielJour I uploaded the full code now, Vertex is a part of a polygon. – Tjernquist1 Feb 15 '16 at 22:31
  • Really appriciate the tips @PaulMcKenzie. I was under the impression that my compexch()-metod in SortedVector was a copy constructor, but I must have gotten the basics of copy constuctors completely wrong from what I see now. – Tjernquist1 Feb 15 '16 at 22:32
  • 1
    Reduction: If I comment both `delete[]`s, the Polygons are displayed correctly. – Gassa Feb 15 '16 at 22:34
  • @Gassa No, don't comment out anything. The OP has to fix the code so that those calls to `delete []` do not cause an issue. – PaulMcKenzie Feb 15 '16 at 22:36
  • 1
    `Polygon` is missing a **copy constructor.** – Daniel Jour Feb 15 '16 at 22:38
  • @Gassa I see, however as PaulMcKenzie says, it seems that there is a underlying problem that causes my program to not work properly, I'd preferably like to solve that but I appreciate the help! – Tjernquist1 Feb 15 '16 at 22:38
  • @PaulMcKenzie Sorry, I don't get it. If the Polygons are displayed correctly without `delete[]`s but incorrectly with `delete[]`s, then the calls to `delete[]` are at least a part of the problem. If that doesn't sound right to you, please explain. – Gassa Feb 15 '16 at 22:38
  • @DanielJour I assume the one in SortedVector (compexch() ) is not a proper copy constructor? – Tjernquist1 Feb 15 '16 at 22:40
  • @Gassa I see, could the problem be that I am not using the delete[] properly? i.e. deleting at the wrong time/deleting too early? – Tjernquist1 Feb 15 '16 at 22:41
  • 1
    @Gassa The `delete` calls are doing a service -- they are exposing the bugs in the class. All you're doing by commenting them out is sweeping the dirt under the rug, and claiming that the room is clean. – PaulMcKenzie Feb 15 '16 at 22:41
  • 1
    @Tjernquist1 -- There is nothing wrong with the `delete` calls. – PaulMcKenzie Feb 15 '16 at 22:42
  • Well, never mind, as @DanielJour seems to have nailed the underlying problem already. Adding a copy constructor and an assignment operator to `Polygon` leads to displaying the `Polygon`s already. Though for me, the program crashes afterwards anyway. – Gassa Feb 15 '16 at 22:45
  • @PaulMcKenzie No, I'm not commenting the `delete[]`s forever and saying "now it works", I sure didn't intend to express such idea. The purpose is to narrow the search by observing the effect of adding or removing them. Oh well. – Gassa Feb 15 '16 at 22:48
  • 1
    Run your program in a memory bounds checker like valgrind. It'll show you where you're using bad memory, which is surely the source of this. – xaxxon Feb 15 '16 at 23:03
  • @Tjernquist1 if you're not allowed to use `std::vector` to store `Polygon`'s vertex list, then the second-best option is to write your own vector-like container, and then have `Polygon` have your custom vector as member variable. That way you avoid getting memory management code mixed up with your program logic; and Polygon can follow [Rule of Zero](http://en.cppreference.com/w/cpp/language/rule_of_three), eliminating the problem entirely. – M.M Feb 15 '16 at 23:24

1 Answers1

3

Given the code you posted, the issues are clear as to what's wrong.

You're passing Polygon's by value here:

friend bool operator > (Polygon polygon1, Polygon polygon2);
friend bool operator < (Polygon polygon1, Polygon polygon2);

and you're copying and assigning values here in: compexch:

if (y < x) {
    T temp = x;  // copy constructor
    x = y;  // assignment
    y = temp;  // assigment
}

This means that copies will be made, and your Polygon class cannot be copied safely. You will have memory leaks and bugs when calling either of these functions.

You should implement the appropriate copy constructor and assignment operator, whose signatures are:

Polygon(const Polygon& rhs);  // copy constructor
Polygon& operator=(const Polygon& rhs); // assignment operator

Both of these functions should be implemented. Please see the Rule of 3 for this information.

However, for operator < and operator >, you should pass references, not values to these functions:

friend bool operator > (Polygon& polygon1, Polygon& polygon2);
friend bool operator < (Polygon& polygon1, Polygon& polygon2);

Then the copy constructor and assignment operator are not brought into play, since the parameter type is a reference.


Let's try to implement the copy / assignment functions anyway, for completeness:

For example, the copy constructor can be implemented like this:

Polygon::Polygon(const Polygon& rhs) : vertices(new int[rhs.arraySize]), 
                                       arraySize(rhs.arraySize)
{
   for (int i = 0; i < arraySize; ++i)
      vertices[i] = rhs.vertices[i];
}

Then for the assignment operator, using the copy / swap idiom:

Polygon& operator=(const Polygon& rhs)
{
    Polygon temp(rhs);
    std::swap(temp.arraySize, arraySize);
    std::swap(temp.vertices, vertices);
    return *this;
}

Once you've implemented these function, plus the destructor that calls delete[], you should no longer have an issue with copying the objects.


Other issues:

In addition, you really should only overload < and ==, initially with their "full" implementation, and write the other relational operators with respect to these two operators.

Right now, you're making the classic mistake of writing one operator (operator >), and then trying to turn the logic "inside-out" when implementing operator <. What if the logic for operator > were more complex, and it took yeoman's work to figure out what is the "opposite of <"?

If you implemented ==, then operator > just becomes:

return !(polygon1 < polygon2) && !(polygon == polygon2);  // <-- this can be further improved by implementing operator !=
Community
  • 1
  • 1
PaulMcKenzie
  • 34,698
  • 4
  • 24
  • 45
  • There's a copy construction in `compexch`. The OP *needs* a copy constructor. – Daniel Jour Feb 15 '16 at 23:10
  • Amazing answer, really pointed me in the right direction and thoroughly explained the problem and the answer. However I am getting following error when trying to implement the copy-constructor and = operatator. Polygon.cpp: In copy constructor 'Polygon::Polygon(const Polygon&)': Polygon.cpp:129:63: error: cannot convert 'int*' to 'Vertex*' in initialization Why is this? – Tjernquist1 Feb 15 '16 at 23:26
  • 1
    @Tjernquist1 you made a mistake inside your copy constructor, on line 129. Maybe post a new question if you can't resolve it (in which you only need to show the Polygon code, not the rest of the project) – M.M Feb 15 '16 at 23:27
  • I haven't got the program to work yet and I am too tired to do further research for tonight but I'll vote your answer top answer just for the sheer kindness and time you took out to write the answer. Thanks @PaulcKenzie and everybody else! – Tjernquist1 Feb 15 '16 at 23:37
  • I was so excited about getting it to work I couldn't go to bed, after a little bit of tinkering, I got it to work! Huge thanks! – Tjernquist1 Feb 16 '16 at 00:29