【问题标题】:A vector of unique_ptr at specific positions in vector向量中特定位置的 unique_ptr 向量
【发布时间】:2017-12-05 05:34:15
【问题描述】:

我有一个名为 Grid 的类,它由 Cells 组成。每个单元格可以有自己的格式(概念类似于 MS Excel)。

Grid 中的格式保存在一个向量 std::vector<std::unique_ptr<CellFormat>> m_CellFormatTable 中,它拥有所有格式,所以每当我需要读取 Cells 格式时,我都会从向量中读取它,只要有变化,就会报告给向量。抱歉,我对 C++11 标准很陌生,所以我的想法可能是错误的。

由于网格是一个矩阵,当一个单元格的格式发生变化时,每个单元格都属于矩阵的不同部分,它应该反映在矩阵的正确部分,即在向量中正确定位(CellFormatTable) .所以现阶段我不能使用vector的push_back方法。

CellFormat 类:

struct CellFormat
{
    wxFont m_Font;
    wxColor m_BackgroundColor, m_TextColor;
    int m_HorizontalAlignment, m_VerticalAlignment;

    CellFormat(Grid* ws) {
        m_BackgroundColor = ws->GetDefaultCellBackgroundColour();
        m_TextColor=ws->GetDefaultCellTextColour();
        int horizontal = 0, vertical = 0;
        ws->GetDefaultCellAlignment(&horizontal, &vertical);
    }

    CellFormat(const CellFormat& other) {
        m_Font = other.m_Font;
        m_BackgroundColor = other.m_BackgroundColor;
        m_TextColor = other.m_TextColor;
        m_HorizontalAlignment = other.m_HorizontalAlignment;
        m_VerticalAlignment = other.m_VerticalAlignment;
    }

    CellFormat& operator=(const CellFormat& other) {
        if (this == &other) return *this;
        m_Font = other.m_Font;
        m_BackgroundColor = other.m_BackgroundColor;
        m_TextColor = other.m_TextColor;
        m_HorizontalAlignment = other.m_HorizontalAlignment;
        m_VerticalAlignment = other.m_VerticalAlignment;

        return *this;
    }
};

在 Grid.h 中

class Grid{
    std::vector<std::unique_ptr<CellFormat>> m_CellFormatTable;
    //
    CellFormat* GetCellFormat(int row, int column);
    void SetCellFormat(int row, int column, CellFormat format);
    void ApplyCellFormat(int row, int column, const CellFormat* format);
    CellFormat* CreateCellFormat(int row, int column);
    //rest is omitted
}

在 Grid.cpp 中

Grid(some arguments){
    m_CellFormatTable.resize(nrows*ncols);
    //rest is omitted
}

CellFormat* Grid::GetCellFormat(int row, int column)
{
    int ncols= GetNumberCols();

    return m_CellFormatTable[row*ncols+ column].get();
}

void Grid::SetCellFormat(int row, int column, CellFormat other)
{
    CellFormat* format = GetCellFormat(row, column);
    if (format == 0) format = CreateCellFormat(row, column);
    *format = other;
}

void Grid::ApplyCellFormat(int row, int column, const CellFormat * format)
{
    if (format == 0) {
        int ncols= GetNumberCols();
        //Set everything to default values
        //Omitted

        m_CellFormatTable[row*ncols+ column].reset();
    }
    else {
        wxColor bgcolor = format->m_BackgroundColor;
        if (bgcolor.IsOk()) SetCellBackgroundColour(row, column, bgcolor);
        SetCellTextColour(row, column, format->m_TextColor);
        SetCellFont(row, column, format->m_Font);
        SetCellAlignment(row, column, format->m_HorizontalAlignment, format->m_VerticalAlignment);
    }
}

CellFormat* Grid::CreateCellFormat(int row, int column)
{
    int ncols= GetNumberCols();
    CellFormat* format = new CellFormat(this);
    m_CellFormatTable.emplace(m_CellFormatTable.begin() + row*ncols+ column, std::move(format));

    return format;
}

每当我格式化一个单元格时,说它的背景颜色发生了变化,我都会使用以下尝试:

CellFormat* format = ws->GetCellFormat(row, col);
if (format == 0) format = ws->CreateCellFormat(row, col);

if (ChangeFillColor) {
    ws->SetCellBackgroundColour(row, col, m_LastChosenFillColor);
    format->m_BackgroundColor = m_LastChosenFillColor;
}

代码在format-&gt;m_BackgroundColor 处的ApplyCellFormat 函数处失败,因为应该是单元格背景颜色的颜色无效。这告诉我,CreateCellFormat 很可能没有将 CellFormat 放在正确的位置。我尝试使用 insert 而不是 emplace 但编译器(VS 2015)抱怨我所有的尝试。

任何想法表示赞赏。

【问题讨论】:

  • 尝试将所有这些代码墙归结为您问题的本质。不清楚的是,矩阵是否应该是满的(即所有有效的对 i,j 都被一个单元格占用)。
  • @macroland,你为什么使用自己的网格和表格类? wxWidgets 已经具备了所有这些功能,包括定制网格表的可能性......
  • 请将解决方案发布为答案,而不是更新您的问题。这是为了帮助未来的访客并避免混淆。谢谢。

标签: c++ c++11 wxwidgets


【解决方案1】:

你有几个问题。
一种是您添加了CellFormat*,但您的向量存储了unique_ptr;因此您需要std::make_unique使用新格式。

问题:您确定需要指针向量而不是对象吗?

其他是您假设向量包含所有单元格的所有数据,如果尚未设置,则为 0。那是错误的。向量的元素数量与您“推送”或“放置”的数量一样多。
假设您已经“推送”了单元格 (0,0) 的格式。现在您要设置 (5,2) 的格式,即(假设您有 10 个列)向量中的第 52 个元素,但您只有一个。所以vector[51] 是未定义的(vector.at(51) 会引发错误)。
首先添加所有单元格格式,一些值 = 0 表示尚未设置。或者重新考虑你的策略。

顺便说一句,您可以使用wxGridCellAttr,它提供您自己编码的内容。

【讨论】:

  • 所以您认为即使我的get 方法尝试使用emplace 检索应该放置在那里的第52 个元素,也会出现错误,因为向量中只有一个元素?跨度>
【解决方案2】:

根据您使用unique_ptrvector(而不是对象)这一事实,我推断并非矩阵的所有元素都被实际占用。在这种情况下,最好使用std::map(如果矩阵非常大,则使用std::unordered_map)对象(而不是unique_ptrs)。

template<typename T>
struct grid
{
    using index = std::pair<unsigned, unsigned>;

    // insert element if not already present
    // returns if insertion occurred
    template<typename...Args>
    bool insert(index const&i, Args&&...args)
    {
        return data.emplace(std::forward<Args>(args)...).second;
    }

    // remove element (if it exists)
    void remove(index const&i)
    {
        data.erase(i);
    }

    // get pointer to element, may be nullptr
    T* get(index const&i)
    {
        auto it = data.find(i);
        return it==data.end() ?
            nullptr : std::addressof(*it);
    }
  private:
    std::map<index,T> data;
};

【讨论】:

  • 事实上你是对的,并不是所有的单元格都被格式化(除了默认格式),因此并不是矩阵的所有元素都被实际占用。我会尝试这种方法。
【解决方案3】:

我看到您的代码在这部分代码中失败的原因:

CellFormat* format = ws->GetCellFormat(row, col);
if (format == 0) format = ws->CreateCellFormat(row, col);

if (ChangeFillColor) {
    ws->SetCellBackgroundColour(row, col, m_LastChosenFillColor);
    format->m_BackgroundColor = m_LastChosenFillColor;
}

是由于你的类是如何定义的:

class Grid{
    std::vector<std::unique_ptr<CellFormat>> m_CellFormatTable;
    //
    CellFormat* GetCellFormat(int row, int column);
    void SetCellFormat(int row, int column, CellFormat format);
    void ApplyCellFormat(int row, int column, const CellFormat* format);
    CellFormat* CreateCellFormat(int row, int column);
    //rest is omitted
}

默认情况下,您的类的成员和函数设置为 private:

把你的班级改成这样:

class Grid {
public:
    typedef std::vector<std::unique_ptr<CellFormat>> Format;
private:
    Format m_CellFormatTable;

public:
    CellFormat* getCellFormat( int row, int column );
    void        setCellFormat( int row, int column, const CellFormat& format );
    void        applyCellFormat( int row, int column, const CellFormat& format );

   // Add This Function If Needed
   Format getCellFormatTable() const { return m_CellFormatTable; }
};

所以你的类的成员函数被声明为public: 然后外部和非友元对象现在可以访问这个类的成员函数并能够通过get方法返回数据结构。

【讨论】:

    【解决方案4】:

    感谢所有有用的 cmets 和帖子。最后它按预期工作。

    正如 Ripi2 所建议的,向量中的 CellFormat 对象没有被初始化,所以我在构造函数中初始化了它们。这里也没有介绍,我在代码中的某处有一个未初始化的对象向量,因此也更正了该部分。

    虽然循环遍历网格的所有行和列并创建默认格式并不是最好的主意,但 Walter 的建议是我未来的工作,即使用集合。

    Grid(some arguments){
        for (int i = 0; i < nrows*ncols; i++) {
            m_CellFormatTable.emplace_back(new CellFormat(this));
        }
       //Rest is omitted
    }
    

    下面的代码也更正了:

    CellFormat* Grid::CreateCellFormat(int row, int column)
    {
        int ncols = GetNumberCols();
        CellFormat* format = new CellFormat(this);
        std::unique_ptr<CellFormat> ptr(format);
        m_CellFormatTable.emplace(m_CellFormatTable.begin() + row*ncols + column,std::move(ptr));
    
        return format;
    }
    

    跟踪格式的一种方法是:

    CellFormat* format = ws->GetCellFormat(i, j);
    if (ChangeFillColor) {
        ws->SetCellBackgroundColour(i, j, m_LastChosenFillColor);
        format->m_BackgroundColor = m_LastChosenFillColor;
    }
    

    【讨论】:

    • 您应该考虑将 wxGridCellAttr 与 wxGrid 一起使用,作为已接受的答案。
    • 感谢您的评论,我会考虑使用 wxGridCellAttr 但我在其他一些功能上使用m_CellFormatTable,到目前为止,这对我来说很容易处理。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-07-21
    • 1970-01-01
    • 2017-09-10
    • 2012-11-19
    • 2014-05-23
    • 1970-01-01
    • 2021-05-16
    相关资源
    最近更新 更多