【问题标题】:C++: How to implement polymorphic object creator to populate a tableC++:如何实现多态对象创建器来填充表
【发布时间】:2020-09-04 13:25:43
【问题描述】:

我有一个可以使用set_num_col(int)set_num_row(int) 调整大小的表格小部件对象。 对这些函数中的每一个的调用将调用resize_table() 函数以使用table_cells 对象填充小部件。 但是,我有两种多态类型的单元格:table_cell_defaulttable_cell_custom,它们派生自同一个基类。 在创建表格时,考虑到客户端知道哪些单元格是自定义的以及哪些是默认类型,我如何使用混合类型的单元格填充它?

我考虑在表类中添加一个映射,并使用例如set_custom_cells( vector<index>() ) 填充此映射,将单元格的ij 索引作为键,并且相应的 lamda 创建者返回正确的类型作为值,但这map 只会被使用一次来填充表格,并且永远不会再次使用。有没有更动态的方法,使用 lambda 作为 table_cell 创建器来更好地填充该小部件?

谢谢

【问题讨论】:

  • 您能否让创建表格小部件的调用者传入用于构造表格中的单元格对象的工厂 lambda,并传入 col 和 row 参数?这样,调用者就可以弄清楚(如 lambda 表达式所示)如何初始化表格的单元格。
  • @Eljay 感谢您的评论!你能帮我看看工厂 lambda 的样子吗?提前谢谢你

标签: c++ polymorphism


【解决方案1】:

这是一个使用 factory lambda 在表的构造函数中生成初始单元格的示例。 lambda所在的函数参考main函数,使用方法参考Table构造函数。

我不知道你的代码是什么样子的,所以我只是将每个单元格包装成一个 object_t 并将其放入表中。

#include <cstdint>
#include <functional>
#include <iostream>
#include <memory>
#include <sstream>
#include <string>
#include <vector>

// Not idempotent.  Should be last include.
#include <cassert>

using std::cout;
using std::function;
using std::make_shared;
using std::move;
using std::ostream;
using std::shared_ptr;
using std::size_t;
using std::string;
using std::stringstream;
using std::vector;

namespace {

template <typename T>
void draw_right_justified(T const& x, ostream& out, size_t width) {
    stringstream ss;
    ss << x;
    string s = ss.str();
    size_t pad_width = s.length() < width ? width - s.length() : 1;
    out << string(pad_width, ' ') << s;
}

class object_t {
public:
    template <typename T>
    object_t(T x) : self_{make_shared<model<T>>(move(x))}
    { }

    friend void draw_right_justified(object_t const& x, ostream& out, size_t width) {
        x.self_->draw_right_justified_thunk(out, width);
    }

private:
    struct concept_t {
        virtual ~concept_t() = default;
        virtual void draw_right_justified_thunk(ostream&, size_t) const = 0;
    };

    template <typename T>
    struct model : concept_t {
        model(T x) : data_{move(x)} { }

        void draw_right_justified_thunk(ostream& out, size_t width) const {
            draw_right_justified(data_, out, width);
        }

        T data_;
    };

    shared_ptr<const concept_t> self_;
};

class Table {
    size_t col;
    size_t row;
    // data will be constructed with col_ * row_ entries.
    vector<object_t> data;
public:
    using object_factory = function<object_t(size_t, size_t)>;
    Table(size_t col_, size_t row_, object_factory& fn);
    auto operator()(size_t x, size_t y) const -> object_t;
    void display(ostream& out) const;
};

Table::Table(size_t col_, size_t row_, Table::object_factory& fn)
    : col{col_}, row{row_}
{
    data.reserve(col * row);
    for (size_t y = 0; y < row; ++y) {
        for (size_t x = 0; x < row; ++x) {
            data.emplace_back(fn(x, y));
        }
    }
}

object_t Table::operator()(size_t x, size_t y) const {
    assert(x < col);
    assert(y < row);
    return data[y * row + x];
}

void Table::display(ostream& out) const {
    auto const& self = *this;
    for (size_t y = 0; y < row; ++y) {
        for (size_t x = 0; x < col; ++x) {
            draw_right_justified(self(x, y), out, 8);
        }
        out << "\n";
    }
}

struct empty_t {};

void draw_right_justified(empty_t, ostream& out, size_t width) {
    string s = "(empty)";
    size_t pad_width = s.length() < width ? width - s.length() : 1;
    out << string(pad_width, ' ') << s;
}

struct bunny { string name; };

void draw_right_justified(bunny const& bunny, ostream& out, size_t width) {
    auto const& s = bunny.name;
    size_t pad_width = s.length() < width ? width - s.length() : 1;
    out << string(pad_width, ' ') << s;
}

} // anon

int main() {
    Table::object_factory maker = [](size_t x, size_t y) {
        if (x == 0 && y == 1) return object_t{bunny{"Bugs"}};
        if (x == 0 && y == 0) return object_t{empty_t{}};
        if (x == y) return object_t{string("EQUAL")};
        return object_t{x * y};
    };

    auto table = Table{3, 5, maker};
    table.display(cout);
}

输出...

 (empty)       0       0
    Bugs   EQUAL       2
       0       2   EQUAL
       0       3       6
       0       4       8

【讨论】:

  • 非常感谢!这么明显的例子!我真的很感激
  • 我使用了 Sean Parent 的静态多态技术。动态多态性是侵入性的,并且不可能将虚函数添加到(比如说)int。不幸的是,静态多态性需要一些样板基础设施代码。我没有花时间重构这个代码示例中的所有技术债务。
猜你喜欢
  • 2016-09-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多