尽管有构造函数,但仍在具有未初始化属性的类上调用方法

时间:2019-03-08 23:41:27

标签: c++ debugging

前提::假设我在该子集中有一个2D空间的矩形子集和一个点集合,这些点都具有不同的x值。为了优化尚未编写的算法,我想按照以下过程将框拆分为单元格:我将矩形沿x轴分成两等份。然后,我将每个子矩形重复减半,直到每个单元格包含1个点或根本不包含任何点。Example

在此示例中,垂直线表示“减半过程”,并且这些线是按黑暗排序的(较暗的是较新的)。

首先,我将定义两个基本类:

class Point{
    private:
        double x;
        double y;
    public:
        // [...]
        // the relevant constructor and getter
        // overloaded operators +, -, * for vector calculations
};

class Box{
    private:
        Point bottom_left_point;
        double width;
        double height;
    public:
        Box(Point my_point, double my_x, double my_y) : // constructor
            bottom_left_point(my_point), width(my_x), height(my_y){}

        bool contains(const Point& p); // returns true iff the box contains p in the geometric sense
        Box halve(bool b) const; // takes a boolean as input and returns the left half-rectangle for false, and the right half-rectangle for true
};

现在要实现“减半算法”,我需要一个类似于二叉树的结构。每个节点将代表矩形的一个子单元(根节点代表整个矩形)。一个节点可能有两个孩子,在这种情况下,孩子代表其左右两半。节点还可以具有指向单元中存在的粒子的指针。最终的想法是从一棵空树开始,然后使用方法insert(Point* to_be_inserted)一一插入点。

因此,我将定义以下递归类,其private属性相当不言自明:

class Node;

class Node{
    private:
        enum node_type{ INT, EXT, EMPTY };

        node_type type;
        // type == INT means that it is an interior node, i.e. has children
        // type == EXT means that it is an exterior node, i.e. has no children but contains a point
        // type == EMPTY means that it has no children and no point

        std::array<Node*,2> children;
        Box domain; // the geometric region which is being represented
        Point* tenant; // address of the particle that exists in this cell (if one such exists)

    public:
        Node(Box my_domain) :
            type(EMPTY), children({nullptr}), domain(my_domain){}
        //
        // to be continued...

首要任务是定义一个subdivide()方法,该方法赋予我的节点两个孩子:

void Node::subdivide(void){
    type = INT;
    children[0] = new Node(domain.halve(false));
    children[1] = new Node(domain.halve(true));
}

现在一切就绪,可以写出整个问题的症结,insert方法。由于将以递归方式编写,因此最简单的解决方案是使用布尔返回类型,该类型告诉我们插入是成功还是失败。考虑到这一点,下面是代码:

bool Node::insert(Point* to_be_inserted){
    if(not domain.contains(*to_be_inserted)) return false;
    switch(type){
        case INT:{
            for(Node* child : children) if(child->insert(to_be_inserted)) return true;
            return false;
        }
        case EXT:{
            subdivide();

            for(Node* child : children) if(child->insert(to_be_inserted)) break;
            tenant = nullptr;

            for(Node* child : children) if(child->insert(to_be_inserted)) return true;
            break;
        }
        case EMPTY:{    
            type = EXT;
            tenant = to_be_inserted;
            return true;
        }
    }
    throw 1; // this line should not, in, theory ever be reached
}

(请注意,为了抽象和通用起见,当我可以简单地写出这两种情况时,就在数组for上使用了children循环。)

说明:

  • 首先,我们检查to_be_inserted是否在this表示的几何区域中。如果不是,请返回false

  • 如果this是内部节点,则将点传递给每个子节点,直到成功插入为止。

  • 如果this是一个外部节点,则意味着我们必须将该节点分为两部分,以便能够将to_be_inserted与当前存在于该节点中的点正确隔离

    • 首先我们叫multiply()
    • 然后,我们尝试将当前的租户insert租给其中一个孩子(请原谅这听起来多么淫秽,我向您保证这不是故意的)。
    • 完成后,我们对to_be_inserted执行相同的操作并返回结果。 (请注意,由于对box::contains的初步调用,因此先验插入此时将是成功的。
  • 最后,如果thisEMPTY节点,我们只需要将tenant分配给*to_be_inserted并将type更改为{{1 }},我们就完成了。

好吧,让我们用一个简单的EXT来尝试一下:

main

这可以编译,但是在运行几次之后,在运行int main(void){ Box my_box(ORIGIN, 1.0, 1.0); // rectangle of vertices (0,0),(0,1),(1,0),(1,1) Node tree(box); // initializes an empty tree representing the region of my_box Point p(0.1, 0.1); Point q(0.6, 0.7); tree.insert(&p); tree.insert(&q); return 0; } 底部的异常时会抛出该异常。假设在任何时候都没有insert值构造Node,这怎么可能?

编辑:除了这一点,我还注​​意到了一些可能的错误,这些错误也可能由于代码的少量更改而发生:

  • 无法解释的对type

  • 的调用
  • 通过地址nullptr->insert(something)insert的调用,该地址未指向已初始化的0x0000000000000018


可以在https://github.com/raphael-vock/phantom-call上找到整个代码,包括带有相关调试标志的Node

0 个答案:

没有答案