0

I'm new to C++ and currently practicing on a Singly Linked List. Somehow the output of the code below is always zero. I think the problem is the nextPoint Method but however I try to change the reference/dereference, it doesn't work.

Where is the problem? Thank you in advance.

// Singly Linked List

#include <math.h>
#include <iostream>

class Point {

        public:
        double x, y;
        Point* next;

        // constructor
        Point (double x, double y) {
                this->x = x;
                this->y = y;
                this->next = NULL;
        }

        void nextPoint(Point nexti) {
                this->next = &nexti;
        }

        double dist(Point &a, Point &b) {
                double dx = a.x - b.x;
                double dy = a.y - b.y;
                return sqrt(dx*dx - dy*dy);
        }

        double length() {
                Point *iter = this;
                double len = 0.0;
                while (iter->next != NULL) {
                        len += dist(*iter, *iter->next);
                        iter = iter->next;
                }
                return len;
        }

};

int main() {

        Point p1(1,1);
        Point p2(2,2);
        Point p3(5,5);
        p1.nextPoint(p2);
        p2.nextPoint(p3);

        std::cout << p1.length() << std::endl;

        return 1;

}
rugkei
  • 70
  • 7
  • 2
    You're using `Point &` for `a` and `b`, but `Point` for `nexti`. Do you understand what each of the constructs means? What made you select the parameter types the way you did? – Angew is no longer proud of SO Dec 27 '17 at 12:52
  • 2
    BTW, if you're learning C++, a [good book](https://stackoverflow.com/q/388242/1782465) might come in handy. – Angew is no longer proud of SO Dec 27 '17 at 12:53
  • `Point &` is the objects address so pass-by-reference and just `Point` is pass-by-value, right? Because I didn't want to copy the whole Point object I used `&` in `dist()`. Because the `nextPoint()` wasn't working I changed them up a bit. – rugkei Dec 27 '17 at 13:14
  • 2
    More or less (`Point&` is a reference to a `Point`, not really an address). That was a leading question, though. Why are you passing into the `nextPoint` function by value? – Angew is no longer proud of SO Dec 27 '17 at 13:16
  • In the beginning I also used & but because it hasn't worked I changed it. – rugkei Dec 27 '17 at 13:20
  • 2
    Randomly adding/removing pointers/references/dereferences/whatever by trial and error is not a way to learn programming, I'm afraid (or at least to be any good at it). – Angew is no longer proud of SO Dec 27 '17 at 13:22

2 Answers2

3

Please turn on more compiler warnings and you'll probably get a warning that in nextPoint you are storing the address of a temporary variable (nexti) permanently (in this->next).

You must either pass the address of or a reference to the point to add.

void nextPoint(Point *nexti) {
    this->next = nexti;
}

p1.nextPoint(&p2);
p2.nextPoint(&p3);

or

void nextPoint(Point &nexti) {
    this->next = &nexti;
}

p1.nextPoint(p2);
p2.nextPoint(p3);

Side note: please replace NULL with nullptr.

Werner Henze
  • 16,404
  • 12
  • 44
  • 69
  • For me, both of your solutions still give the result 0. To compile I use `g++ file.cpp -o file`. Should I add the flag `-Wall` or should I even change compiler? – rugkei Dec 27 '17 at 13:17
  • 2
    No, don't try to blame the compiler for this... It's almost never the compiler. And yes, you should turn on warnings; IMO, set `-Wall -Wextra -Wpedantic` and never look back. – underscore_d Dec 27 '17 at 13:17
  • @underscore_d Okay, thank you. Because of my choice of points the result was always 0. Furthermore, my euclidean formula was wrong.... – rugkei Dec 27 '17 at 13:34
  • I agree with Werner's answer, I would like to add something to your implementation A better implementation would be to name this class LinkedList and create a struct called Node for storing variables (x,y) and a Node* (a pointer pointing to the next element) Check [this](https://www.geeksforgeeks.org/linked-list-set-1-introduction/) implementation of LinkedList – Shreyas Pimpalgaonkar Dec 27 '17 at 13:08
3

There are two problems with your code:

  1. nextPoint takes its parameter by value, which means you're storing the address of that by-value parameter which becomes invalid as soon as the execution of nextPoint ends. Change it to accept Point &nexti.

  2. Your distance computation function is wrong. You should be adding the squares, not subtracting them: return sqrt(dx*dx + dy*dy);


Unrelated to your question, but there are several ways in which you could improve your code:

  • Use the mem-initialiser list in the constructor to initialise members instead of assigning to them. This is a good habit to get into, as it will come useful once you start dealing with things where initialisation and assignment are substantially different (references, classes, ...).

    Point (double x, double y) : x(x), y(y), next(nullptr)
    {}
    
  • Use nullptr instead of NULL, since the latter is not type-safe.

  • length should be marked const, because it does not modify the object on which it's called. Note that iter has likewise been changed to const Point *:

    double length() const {
            const Point *iter = this;
            double len = 0.0;
            while (iter->next != NULL) {
                    len += dist(*iter, *iter->next);
                    iter = iter->next;
            }
            return len;
    }
    
  • dist does not use this at all, and so it could (and should) be made a static member function. Also, it should take its parameters by const &, because it doesn't modify them:

    static double dist(const Point &a, const Point &b) {
            double dx = a.x - b.x;
            double dy = a.y - b.y;
            return sqrt(dx*dx - dy*dy);
    }
    
Angew is no longer proud of SO
  • 167,307
  • 17
  • 350
  • 455
  • 1
    Another reason the init-list is preferable, even if initialisation and assignment achieve the same end result, is that assigning to a member in the body means its default constructor is executed first, which is totally redundant as you then assign and replace what the default constructor did. This can cause non-trivial overhead for custom types. Using the init-list wherever possible is good practice because it avoids various kinds of pitfall and leads to more consistent/maintainable code even where no such pitfalls (currently) exist. – underscore_d Dec 27 '17 at 13:52
  • @underscore_d I actually consider (not) invoking the default constructor as "substantially different." – Angew is no longer proud of SO Dec 27 '17 at 13:53
  • @Angew Thank you for your detailed answer. I see that using `nullptr` is necessary because `NULL` is not type-safe. However, what benefits does it bring to use `const`? – rugkei Dec 27 '17 at 14:19
  • @rugkei Too big a question to handle in a comment; any decent C++ book will cover this, and even uncle Google should tell you something. One potential starting point is [the C++ FAQ](https://isocpp.org/wiki/faq/const-correctness). Just one point to mention: temporaries can bind to `const T &`, but not to `T &`. – Angew is no longer proud of SO Dec 27 '17 at 14:22