1

I belive the answer to this question is really simple, but I just can't get this thing working properly. I have basically created two classes; one for points and one for polygons. Polygons consists of a dynamic list of points.

However, when i try to overload the + operator in the points class and make it return a polygon of the two points i get som weird output and a "Debug assertion failed" after i close the console window.

Here is the + operator overloading method:

CPolygon CPoint::operator + (CPoint pointToAdd) {
    CPolygon polygon;
    polygon.addPoint(this);
    polygon.addPoint(pointToAdd);

    cout << polygon.toString();

    return polygon;
}

When i now try to use this method I get the following output for instance:

(5, 2, 3) - (1, 1, 2)

(444417074, -33686019, , -1555471217) - (-1424299942, 0, 0)

The first of the ouputed lines is from the method itself, whereas the 2nd line is from the place where the polygon is being returned to.

I really have noe idea what is happening to my polygon object on the way from being inside the method to its returning way to the calling code.

I'd be very thankful if anyone could give me a bit of insight on this one :)

EDIT

Here are the addPoint methods of the polygon class:

void CPolygon::addPoint(CPoint pointToAdd) {
    nrOfPoints++;

    // Create a temp array of the new size
    CPoint * tempPoints = new CPoint[nrOfPoints];

    // Copy all points to the temp array
    for(int i = 0; i < (nrOfPoints - 1); i++) {
        tempPoints[i] = points[i];
    }

    // Add the new point to the end of the array
    tempPoints[nrOfPoints - 1] = pointToAdd;

    // Delete the old array and set the temp array as the new array
    delete[] points;
    points = tempPoints;
}

void CPolygon::addPoint(CPoint * pointToAdd) {
    addPoint(* pointToAdd);
}
Terskel
  • 73
  • 1
  • 8
  • 1
    What is o addPoint and CPolygon and the values you pass in – mmmmmm Oct 09 '13 at 09:14
  • 2
    Shouldn't it be `polygon.addPoint(*this);`? Or do you have both a `addPoint(const CPoint &)` and a `addPoint(const CPoint *)` (or the non-`const` versions)? – Bernhard Barker Oct 09 '13 at 09:15
  • 3
    since we dont know anything about the type, a wild guess: you did not obey the rule of three – PlasmaHH Oct 09 '13 at 09:15
  • 1
    Have you implemented a copy constructor for your polygon? The return takes a copy! – urzeit Oct 09 '13 at 09:16
  • 1
    Preferably change your method signature to `CPolygon CPoint::operator + (const CPoint &pointToAdd)` otherwise you create an unnecessary copy of the parameter. – Bernhard Barker Oct 09 '13 at 09:18
  • Thank you for your answers, I have updated my post. To Plasma and urzeit: No, I have not implemented a copy constructor. I thought that wouldn't be necessary in this situation? :/ – Terskel Oct 09 '13 at 09:26
  • Note: the passed object should be by reference or pointers and the return of object from method as you write cause calling the copy constructor and in large objects these are performance issues – ahmedsafan86 Oct 09 '13 at 09:29
  • 2
    Meh. Ugh. Eww. `std::vector`. – R. Martinho Fernandes Oct 09 '13 at 09:34
  • Can you post the entire code, because the issue seems outside of the posted. – Skeen Oct 09 '13 at 09:56
  • @Terskel: You're passing by value to the function, and returning a value from it, both of which will use the copy constructor; and it looks like your class is managing memory by juggling raw pointers and hoping for the best, in which case you most definitely need the [big three](http://stackoverflow.com/questions/4172722). Why not make life easy for yourself, and use `std::vector` to manage your dynamic arrays? – Mike Seymour Oct 09 '13 at 10:03

3 Answers3

1

I did as PlasmaHH and urzeit suggested above and implemented the copy constructor for the polygon class, and guess what, it solved the problem! :) Thanks to everyone who has been helping me out!

The copy constructor for the polygon class looks like this:

CPolygon::CPolygon(const CPolygon & polygon) :
    nrOfPoints(polygon.nrOfPoints)
{
    points = new CPoint[nrOfPoints];

    // Add all the points from the polygon to be copied
    for(int i = 0; i < nrOfPoints; i++) {
        points[i] = polygon.points[i];
    }
}
Terskel
  • 73
  • 1
  • 8
  • 1
    You should be implementing the move constructor as well, as using that for your local-variable return, it'll yield better performance, also it's likely that using an std::vector would do so, at least it'll clean up your code a bit. – Skeen Oct 09 '13 at 10:15
0

Your code:

CPolygon CPoint::operator + (CPoint pointToAdd) {
    CPolygon polygon;


    polygon.addPoint(this);
    polygon.addPoint(pointToAdd);

    cout << polygon.toString();

    return polygon;
}

I see strange that for polygon.addPoint(this) you are adding a pointer to CPolygon, since this is a pointer. Should you use polygon.addPoint(*this) instead? In polygon.addPoint(pointToAdd) you are adding by value or reference.

If you need further help, please, add all the prototypes for function CPolygon::addPoint.

Germán Diago
  • 7,473
  • 1
  • 36
  • 59
  • Please see my edited post. There you will see that I have two addPoint methods, one for pointer, and one for value. – Terskel Oct 09 '13 at 09:28
-1

Your polygon object is declared on stack, you will lose reference to it after the operator scope!

Try to declare it:

CPolygon* polygon = new Polygon(...);

and your signature should look like:

CPolygon* CPoint::operator + (CPoint pointToAdd) 

Indeed using raw pointers it's a bad idea and you have to take care of it outside the scope, a better solution will be to user smart pointers:

std::auto_ptr<CPolygon> CPoint::operator + (CPoint pointToAdd) 
{
    std::auto_ptr<CPolygon> polygon(new CPolygon);
    // do stuff here
    return polygon;
}

// ...

{
  std::auto_ptr<CPolygon> polygon = firstPoint + secondPoint;
  // working with CPolygon

  // auto_ptr frees polygon 
}
LZR
  • 948
  • 10
  • 28
  • 1. Returning raw pointers confuses ownership, 2. Won't the local variable just be copied out, in the original code? – Skeen Oct 09 '13 at 09:33
  • 1
    "you will lose reference to it" - Return by value doesn't have this problem. – Bernhard Barker Oct 09 '13 at 09:34
  • maybe this link will provide more information: http://www.informit.com/articles/article.aspx?p=25033&seqNum=3 – LZR Oct 09 '13 at 09:45
  • @pavel.lazar: The link is too outdated to take moving into account, which is really you'd like in this situation, and what you'd get, using the incorrect shallow default copy constructor. – Skeen Oct 09 '13 at 09:48
  • @pavel.lazar That article barely mentions return by pointer. It does mention a dangling reference in the case of return by reference, which is the closest thing you're referring to. For return by value it mentions return value optimization, which prevents copying the object. All in all, I'm wondering whether you actually read the whole article. – Bernhard Barker Oct 09 '13 at 10:14
  • @Skeen It does mention return value optimization, which prevents copying. – Bernhard Barker Oct 09 '13 at 10:16
  • @Dukeling: The issues with RVO, is that; 1. It's an optional optimization (which means that you won't know when the compiler will do it, or if' it will, which means the program performance will be highly dependent on the compiler (I agree that it is already) 2. It changes the semantics of the program (which to me at least is troubling), also semantics will now change based on compiler and optimization levels and 3. It cannot be done, if the return is based upon some condition (which is not an issue in this case, but it is in the general case). These are the reasons why I prefer moving! – Skeen Oct 09 '13 at 19:39
  • @Dukeling: Also on a side now, if RVO solved the issue of returning by value, is there any reason why we should have moving in C++11? – Skeen Oct 09 '13 at 19:43
  • @Skeen 1. True, but most modern compilers do it. 2. It changes the semantics only if your copy constructor doesn't create an exact copy. 3. As long as you're returning a local variable, it should always be possible, at least in theory anyway. If you're returning a non-local variable, neither RVO nor move apply. 4. (Speculation) There's probably a move operator for reasons not related to RVO, if it's used here as well, it might just have been a case of replacing separate functionality with functionality used elsewhere (why have RVO if there's move?). – Bernhard Barker Oct 10 '13 at 08:35
  • @Dukeling: 1. This is true, but it is in no way gurenteed and standardized, which is why I prefer move. 2. It changes semantics whenever it is applied, because the call to the copy-constructor will be removed, and so will any side-effects it has, for instance; `avoid_nuclear_war()`, and generally I do not like copy constructors with side-effects, but think about how smart-pointers use side-effects to signal weak-pointers and such. - now you've memory leaked because of an optimization. – Skeen Oct 10 '13 at 09:01
  • 3. It should be, meaning maybe, if the compiler is cleaver and all, moving is more deterministic. Also I do believe you can move from say globals, you'll just wreck that global in the progress. 4. And there is, for instance for perfect forwarding, and the reason to still have RVO, is in my book, for old code, which does not make sure of move construction. – Skeen Oct 10 '13 at 09:02
  • Addition to 3. I do believe that you can move from all prvalues and xvalues ('castable' from rvalues via std::move). Even though moving something 'shared' will possibly break the old 'copy'. – Skeen Oct 10 '13 at 09:12
  • @Skeen 1. I agree, I don't particularly like the uncertainty of whether RVO will happen or not (I was just saying the article mentions it). 2. Any side effects of copying should be reversed in the destructor, or for logging purposes only (e.g. recording how many times the constructors were called). If this is not the case, your copy constructor is severely flawed and any unwanted behaviour is your own fault. You won't leak memory, using RVO there'd only ever be one object. 3. You can move from a global, but this can't be done as an automatic optimization by the compiler if you return by value. – Bernhard Barker Oct 10 '13 at 09:54
  • @Dukeling: 2. I've come to agree with you, side-effects in copy and move constructors is not portable, due to RVO, and therefore as you state unwanted, and having side effects which does not get called when you expect them to is a huge issue, but the answer to this, must simply be to not expect the copy constructor to be called at all. - also it just occurred to me, that modern compilers do replace move-construction with RVO, as this standard permits this as well. Thereby effectively invalidating most of my arguments against it... Oh the irony.. – Skeen Oct 10 '13 at 14:35