为用户定义的类正确调用复制构造函数/赋值运算符

时间:2018-10-13 09:23:47

标签: c++ constructor copy-constructor assignment-operator user-defined-types

我有一个Point类,如下所示:

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);
    }
};

在代码的后面,有:

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 */
}

创建复制构造函数和赋值运算符的原因是要确保上述块的while循环内的行Point curr_point = *(std::min_element(frontier.begin(), frontier.end()));正常工作。

Point类被另一个名为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();
};

其构造函数:

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"};
    }
}

问题是程序在尝试运行时终止。当我运行调试器时,程序在以下行终止:

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

此操作在添加Point的副本构造函数和赋值运算符之后开始。在这种情况下,我无法确定确切的问题。请帮助。

编辑:

自从改变问题的方向以及回答原始问题以来,所有先前的编辑都已删除。

1 个答案:

答案 0 :(得分:1)

此语句map_v[i][j] = Point(j, i, map[i][j]);是您麻烦的根源。让我们解析一下。

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());

因此,语句p1.parent()将产生nullptr。取消引用UB可能会导致段错误。