如何在头文件中声明相同类类型的数组?

时间:2019-06-03 10:49:25

标签: c++

我是c++的新手。我有一个名为QuadTree的课程。现在QuadTree可以包含其他4个QuadTree的数组。它们要么都具有值,要么都将为null。因此,在我的班级头文件QuadTree.h中,如下所示。

    class QuadTree
    {
    public:
        QuadTree();
        ~QuadTree();

        void Clear();

    private:
        QuadTree nodes_[4];

    };

但是此nodes_声明显示错误

'incomplete type is not allowed'.

我也尝试过

  QuadTree* nodes_[4];

然后在构造函数中初始化

nodes_ = new QuadTree[4];

它给我错误'expression must be a modifiable value'. 我可以声明为列表或其他内容。但它的大小是恒定的(总是4)。所以我想使用数组。请帮助我在头文件中声明以及如何在QuadTree.cpp文件中的构造函数中进行初始化。

4 个答案:

答案 0 :(得分:3)

类不能包含其自身的实例。这样做将要求该类无限大,因为每个子对象将包含一个子子对象,该子子对象将包含一个子子子对象,等等。

可以包含的内容是指向其自身类型的对象的指针。由于指针可以为null,因此可以停止无限循环,并且只包含有限数量的对象。

这意味着以下将起作用:

class QuadTree
{
public:
    QuadTree() : nodes_{nullptr}
    {
    }

    ~QuadTree() { delete[] nodes_; }

    // I've deleted these to make a QuadTree non-copyable.
    // You'll need to implement them to do a deep copy if you want that
    QuadTree(const QuadTree& other) = delete;
    QuadTree& operator=(const QuadTree& other) = delete;

    void Clear();

    void split() {
        nodes_ = new QuadTree[4] {
            { /* TODO QuadTree args */ },
            { /* TODO QuadTree args */ },
            { /* TODO QuadTree args */ },
            { /* TODO QuadTree args */ }
        };
    }

private:
    QuadTree* nodes_;
};

std::unique_ptr这样的智能指针可以稍微清理一下内存管理:

class QuadTree
{
public:
    // No longer have to explicitly initialize nodes_ to nullptr
    // since a default-constructed unique_ptr is null
    QuadTree();

    // No longer need to delete[] here; unique_ptr handles that for us
    ~QuadTree();

    // Copy constructor and assignment are now implicitly deleted since
    // unique_ptr is non-copyable.
    // You'll need to implement them to do a deep copy if you want that

    void Clear();

    void split() {
        nodes_.reset(new QuadTree[4] {
            { /* TODO QuadTree args */ },
            { /* TODO QuadTree args */ },
            { /* TODO QuadTree args */ },
            { /* TODO QuadTree args */ }
        });
    }

private:
    std::unique_ptr<QuadTree[]> nodes_;
};

为避免手动进行内存管理,可以使用std::vector<QuadTree>代替原始的QuadTree*。这会带来少量的内存开销,因为每个std::vector都必须跟踪其大小,但是这样一来,您最终遇到内存损坏问题的可能性就会大大降低,因此值得这样做。这些天内存很便宜。

class QuadTree
{
public:
    QuadTree();
    ~QuadTree();

    // QuadTree is now implicitly copyable, since std::vector does a deep-copy by default

    void Clear();

    void split() {
        nodes_ = {
            { /* TODO QuadTree args */ },
            { /* TODO QuadTree args */ },
            { /* TODO QuadTree args */ },
            { /* TODO QuadTree args */ }
        };
    }

private:
    std::vector<QuadTree> nodes_;
};

要避免std::vector的内存开销,同时保持简洁的代码,可以使用静态大小实现自己的类似于矢量的类。但是,编写这样的类超出了SO答案的范围。

答案 1 :(得分:1)

class QuadTree
{
    QuadTree nodes_[4];

这是不可能的,因为如果每个QuadTree包含4个QuadTree,并且所有这些QuadTree总共包含16个QuadTree子级,而所有这些QuadTree总共包含256个...直至无限个QuadTree。您不能具有无限大小的类型。这也是不可能的,因为不可能声明一个不完整类型的数组,并且该类在其定义内是不完整的(完整类上下文除外,但这在这里不适用)。

树是链接的数据结构。因此,您应该使用链接即指针来连接节点:

class QuadTree
{
    QuadTree* nodes_[4];

  

然后在构造函数中初始化

nodes_ = new QuadTree[4];

数组不可分配。

而且,您确实不应该无条件地在QuadTree的构造函数中创建QuadTree的实例,否则您将无限递归,并且会溢出堆栈。

默认初始化的QuadTree可能没有任何子代。换句话说,根据数据结构的设计,子指针应该指向null或哨兵对象。为了初始化一个指向null的指针数组,可以使用值初始化。最简单的方法是使用默认的成员初始化程序:

class QuadTree
{
    QuadTree* nodes_[4] = {};

替代考虑:使用std::unique_ptr可能比裸露的指针更好。

性能注意事项:如果树总是有0或4个子代,则最好在一个数组中创建所有子代,并最好有一个指向该数组的指针。有关更多详细信息,请参见Miles Budnek's answer

答案 2 :(得分:0)

QuadTree* nodes_[4];

这是一个选择-包含四个指向子节点的指针的数组。 另一个选择是

QuadTree *nodes_;

是指向节点数组的指针。您必须分别跟踪长度,但是如果始终为4,就很清楚了,尽管它只允许您没有或只有4个子节点。


nodes_ = new QuadTree[4];

的初始化。它返回一个指向QuadTree四个实例的数组的指针。

在前一种情况下,数组嵌入在对象中,因此您分别初始化每个成员。

首先,应将其初始化为在构造函数中包含四个空指针。你喜欢那样

QuadTree::QuadTree() : nodes_() {}

数组的初始化有点棘手,但是nullptr是这里的默认值,因此显式初始化可以满足您的需要。

然后,当您真正需要孩子时,您只需这样做

nodes_[n] = new QuadTree();

您可能应该有一个参数化构造函数,该构造函数将初始化其他成员,因此您不会从无效对象开始。


然而,在当今时代,您真正应该使用的是

std::unique_ptr<QuadTree> nodes_[4];

smart 指针的数组,它将在您delete父对象时删除子节点,因为在C ++中,您必须管理内存。或者,对于第二个选项,使用

std::vector<QuadTree> nodes_;

vector是一个将管理其长度的数组-但是您必须确保QuadTree具有正确的 move 构造函数。

答案 3 :(得分:-1)

首先,是的,在这种情况下只能使用指针数组。否则,这将是一个无休止的循环。

第二,您不必为数组调用“ new”,因为在创建对象时已经分配了该数组。

因此您可以直接使用

QuadTree::QuadTree() {
    for (int i = 0; i < 4; i++) {
        nodes_[i] = 0;
    }
}

QuadTree::QuadTree() {
    memset(nodes_, 0, sizeof(nodes_));
}