Search code examples
c++constructorcopy-constructorassignment-operatoruser-defined-types

Invoking copy constructor/ assignment operator properly for user defined class


I have a class Point as shown below:

class Point
{
    int x_v = {-1};
    int y_v = {-1};
    int val_v = {0};
    double f_v = {1000000};
    double g_v = {1000000};
    double h_v = {1000000};
    Point* parent_v = nullptr;
public:
    Point(){}
    Point(int x, int y, int val) : x_v{x}, y_v{y}, val_v{val}
    {}
    Point(const Point& p1)
    {
        x_v = p1.x();
        y_v = p1.y();
        val_v = p1.val();
        f_v = p1.f();
        g_v = p1.g();
        h_v = p1.h();
        *(parent_v) = *(p1.parent());
    }
    ~Point(){}

    int val() const
    {
        return val_v;
    }

    int x() const
    {
        return x_v;
    }

    int y() const
    {
        return y_v;
    }

    double f() const
    {
        return f_v;
    }

    double g() const
    {
        return g_v;
    }

    double h() const
    {
        return h_v;
    }

    Point* parent() const
    {
        return parent_v;
    }

    void set_g(double g)
    {
        g_v = g;
        f_v = g_v + h_v;
    }

    void set_h(double h)
    {
        h_v = h;
        f_v = g_v + h_v;
    }

    void set_parent(Point* p)
    {
        parent_v = p;
    }

    Point& operator=(const Point& p1)
    {
        x_v = p1.x();
        y_v = p1.y();
        val_v = p1.val();
        f_v = p1.f();
        g_v = p1.g();
        h_v = p1.h();
        *(parent_v) = *(p1.parent());
        return *this;
    }

    friend bool operator<(const Point& p1, const Point& p2)
    {
        return p1.f() < p2.f();
    }

    friend bool operator==(const Point& p1, const Point& p2)
    {
        std::cout << p1.g() << "\t" << p2.g() << "\n";
        return (p1.x() == p2.x()) && (p1.y() == p2.y()) && (p1.g() == p2.g());
    }

    friend bool operator!=(const Point& p1, const Point& p2)
    {
        return !(p1 == p2);
    }
};

In the later part of the code, there is:

std::set<Point> frontier;
frontier.insert(start_v);
start_v.set_g(0);
std::cout << "start g: " << start_v.g() << "\n";
start_v.set_h(this -> manhattan(start_v));
while(!frontier.empty())
{
    Point curr_point = *(std::min_element(frontier.begin(), frontier.end()));
    std::cout << "curr_point g: " << curr_point.g() << "\n";
    /* Other code */
}

The reason for creating copy constructor and assignment operator is to make sure that the line: Point curr_point = *(std::min_element(frontier.begin(), frontier.end()));, inside the while loop of the above block, work properly.

The class Point is used by another class called Astar:

class Astar
{
    std::vector<std::vector<Point>> map_v;
    int map_x = {0};
    int map_y = {0};
    Point start_v;
    Point end_v;
    std::vector<Point> path_v;
public:
    Astar(std::vector<std::vector<int>>&, std::pair<int, int>&, std::pair<int, int>&);
    bool is_valid(int, int);
    double manhattan(Point&);
    void search();
    std::vector<Point> path();
};

Its constructor:

Astar::Astar(std::vector<std::vector<int>>& map, std::pair<int, int>& start, std::pair<int, int>& end)
{
    map_y = map.size();
    if(map_y)
    {
        map_x = map[0].size();
    }
    if(map_x == 0 || map_y == 0)
    {
        throw std::invalid_argument{"The map is invalid!\n"};
    }
    for(int i = 0; i < map_y; i++)
    {
        map_v.push_back(std::vector<Point>(map_x));
        for(int j = 0; j < map_x; j++)
        {
            map_v[i][j] = Point(j, i, map[i][j]);
        }
    }
    start_v = Point(start.second, start.first, map[start.first][start.second]);
    end_v = Point(end.second, end.first, map[end.first][end.second]);
    if(!is_valid(start_v.x(), start_v.y()))
    {
        throw std::out_of_range{"Start point is out of range!\n"};
    }
    if(!is_valid(end_v.x(), end_v.y()))
    {
        throw std::out_of_range{"End point is out of range!\n"};
    }
}

The problem is that the program terminates when attempted to run. While I ran the debugger, the program terminated in the line:

map_v[i][j] = Point(j, i, map[i][j]);

This started after the copy constructor and assignment operator of Point was added. I am not able to identify the exact problem in this scenario. Kindly help.

Edit:

All the previous edits have been removed since the direction of question changed and since the original question was answered.


Solution

  • This statement map_v[i][j] = Point(j, i, map[i][j]); is the source of your trouble. Let's parse this a little bit.

    Point(j, i, map[i][j]) Creates a temporary object of type Point 
                           at this point, due to the Point constructor
                           member variable parent_v is nullptr
    map_v[i][j] = ....     This uses the overloaded assignment operator. 
                           Go to its definition and at the end you will see 
                           *(parent_v) = *(p1.parent());
    

    So the statement p1.parent() will yield nullptr. Dereferencing that is UB and will, hopefully, seg fault.