【问题标题】:2D vector resizing needs default constructor of the object2D 矢量调整大小需要对象的默认构造函数
【发布时间】:2021-10-01 13:57:36
【问题描述】:

我有一个简单的球类。它的初始化需要一个颜色名称。

Ball::Ball(ColorName color_name) : m_color_name{color_name} {
}

我尝试创建一个二维矢量板来包含一些球。宽度和高度在外面给出。

class Board {
private:
    int m_width;
    int m_height;
    std::vector<std::vector<Ball>> m_grids;

public:
    Board(int width, int height);
    ~Board();
};

Board::Board(int width, int height) : m_width{width}, m_height{height} {
    m_grids.resize(height);
    for (int row_num = 0; row_num < height; row_num ++) {
        m_grids[row_num].resize(width);
    }
}

然后编译器抱怨说球类需要默认构造函数来调整大小。

然后我尝试将其更改为指针。

class Board {
private:
    int m_width;
    int m_height;
    std::vector<std::vector<Ball> *> *m_grids;

public:
    Board(int width, int height);
    ~Board();
};

Board::Board(int width, int height) : m_width{width}, m_height{height} {
    m_grids->resize(height);
    for (int row_num = 0; row_num < height; row_num ++) {
        m_grids->at(row_num)->resize(width);
    }
}

或者

Board::Board(int width, int height) : m_width{width}, m_height{height} {
    m_grids->resize(height, new std::vector<Ball>[width]);
    for (int row_num = 0; row_num < height; row_num ++) {
        m_grids->at(row_num)->resize(width);
    }
}

他们仍然抱怨同样的事情。

到目前为止,我只能为解决方法创建默认构造函数。

Ball::Ball() {
    m_color_name = ColorName::Default;
}

但我真的不喜欢它,因为球不应该是“default_color”。你应该像“黄色”、“粉红色”或我在其他地方定义的某种颜色。

有没有办法避免创建该构造函数?

【问题讨论】:

  • 当你调整向量的大小时,它会将新的Ball 对象添加到向量中。除非您明确说明(例如通过默认构造函数),否则它们怎么可能是特定颜色?
  • 顺便说一句,您将收到m_grids.resize(height, Ball); 的错误,这与默认构造函数的问题无关。第二个参数需要是一个 object,它被复制到所有元素中,而不是类型。
  • std::vector&lt;std::vector&lt;Ball*&gt;&gt; m_grids; 是用于参考的球指针的二维向量。如果您不希望默认对象不使用resize,请使用push_backemplace_back 添加您需要的许多单独的自定义项目。
  • 另外,在Board 构造函数中,如果有人将负值传递给width 和/或height,您还有更多问题。事实上,您不需要widthheight 成员变量,因为vector 通过调用向量的size() 函数知道这些信息。无缘无故地携带无关变量只会让您容易发生错误——您现在需要将这些单独的 widthheight 成员同步到向量知道的实际宽度和高度,我已经看到了简单的错误发生试图保持值同步。
  • @Someprogrammerdude 感谢您指出m_grids.resize(height, Ball);。我在写这个问题时犯了一个错误。

标签: c++ constructor 2d-vector


【解决方案1】:

好吧,如果您不知道新空板上的球应该是什么颜色,C++ 无法帮助您。您没有为该类型提供默认构造函数,因此 C++ 无法知道要使用哪个默认值。您有多种选择。

一个。使用std::optional&lt;ColorName&gt;的板子。在这种情况下,您将有一个明确的“默认”状态,即std::nullopt。但是,您需要小心并始终确保在需要时放置颜色。

两个。不要事先创建板。仅当您知道应该有什么颜色时才创建它。例如。 m_grids[row_num].push_back(ColorName::Whatever); 可以,不需要默认构造函数。

至于指针(无论是原始的还是智能的)——算了吧。在这个任务中你不需要指针。

【讨论】:

    【解决方案2】:

    条目本身应该是指针,而不是使向量指向指针(这没有任何意义)。由于指针可以是 nullptr,因此向量可以调整大小,如下所示:

    std::vector<std::vector<Ball *>> m_grids_bad;
    // Or even better:
    std::vector<std::vector<std::unique_ptr<Ball>>> m_grids;
    
    // You can then assign things like this (for example):
    m_grids[4][5] = std::make_unique<Ball>(ColorName::RED, 5);
    

    我强烈建议您在可能的情况下始终使用std::unique_ptrstd::shared_ptr,因为没有任何反对意见。它可以帮助您在抛出异常时释放内存,或者允许您共享某些内存地址的所有权等。就这样做吧。

    调整向量的大小现在可以如您所愿,因为std::unique_ptr 有一个默认构造函数,它只是将其内部指针初始化为nullptr

    PS:也许我应该详细说明为什么将向量存储在堆上没有意义。 Vectors已经可以将其内容存储在堆上,并在需要时动态重新分配内存。

    另一件事:如果您的元素类型不可移动,指向指针的向量也非常有用。这在多线程情况下会派上用场,在这种情况下,线程有时会使用指向某事物的指针进行初始化,并依赖该事物来准确地保持在内存中的位置。

    【讨论】:

    • 您的总体思路是正确的,关于智能指针优于原始点的 cmets 也是正确的。然而,unique_ptr 在这种情况下是一个巨大的开销——它破坏了电路板的位置并导致很大的内存碎片。正如我在回答中提到的,正确的选择是使用optional
    • @Mikhail 这取决于元素类型到底是什么。当然,在这种情况下,可选的会更可取,但总的来说,将东西存储在堆上有其优势。如果元素只是一组变量,那么是的,局部性是一个问题。我的回答应该是从更广泛和一般的意义上解决这个问题,但我理解你的看法。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-04-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-24
    • 1970-01-01
    相关资源
    最近更新 更多