我的任务是创建一个表,该表将存储在ROM (只读)中,并将用于某些算法(将数字转换为字符串)。但是,有时用户希望在算法中使用该表之前先对其进行编辑。因此,用户必须能够将表复制到RAM中,更改它,然后使用它。
考虑到这一点,我提出了以下代码。注意:这是我的代码的一个简化版本,用来说明解决方案。实际上,RomClass有更多的成员。
表示表中的一行的
struct Row
{
constexpr Row() = default;
constexpr Row(float f, int i) : num_float(f), num_int(i) {}
float num_float = 0;
int num_int = 0;
};表示ROM中表的类(只读):
class RomClass
{
public:
template <std::size_t N>
constexpr RomClass(const Row (&table)[N])
: m_table(table),
m_size(N)
{
}
std::size_t size() const { return m_size; }
const Row& row(std::size_t i) const { assert(m_size > i); return m_table[i]; }
private:
const Row* m_table = nullptr;
std::size_t m_size = 0;
friend class RamClass;
};类,用于编辑表。它将表复制到RAM中,并允许编辑。它还有一个成员,它返回对RomClass的引用,因此它可以与算法一起使用。
class RamClass
{
public:
RamClass(const RomClass& rom);
RamClass(const RamClass& rhs);
RamClass& operator= (RamClass rhs);
void swap(RamClass& rhs);
const RomClass& romClass() const { return m_rom; } // for algorithms that work with RomClass
std::size_t size() const { return m_table.size(); }
Row& row(std::size_t i) { return m_table[i]; }
private:
RomClass m_rom;
std::vector<Row> m_table;
};
inline void swap(RamClass& lhs, RamClass& rhs)
{
lhs.swap(rhs);
}
RamClass::RamClass(const RomClass& rom)
: m_rom(rom) // naredimo kopijo
{
m_table.resize(rom.m_size);
std::copy(&rom.m_table[0], &rom.m_table[rom.m_size], &m_table[0]);
m_rom.m_table = &m_table[0];
}
RamClass::RamClass(const RamClass& rhs)
: m_rom(rhs.m_rom),
m_table(rhs.m_table)
{
const Row*& table = *const_cast<const Row**>(&m_rom.m_table);
table = &m_table[0];
}
RamClass& RamClass::operator=(RamClass rhs)
{
this->swap(rhs);
return(*this);
}
void RamClass::swap(RamClass& rhs)
{
using std::swap;
swap(m_table, rhs.m_table);
auto lhsTable = m_rom.m_table;
auto rhsTable = rhs.m_rom.m_table;
swap(lhsTable, rhsTable);
}Algorith函数看起来类似于:
void algorithm(const RomClass& rom)
{
for (auto i = 0; i < rom.size(); ++i)
{
// do sth
}
}用法:
constexpr Row romTable[] =
{
Row(19.99F, 2),
Row(2.3F, 4),
Row(), // end element
};
constexpr RomClass romClass =
{
romTable
};
int main()
{
RamClass ramClass = romClass;
ramClass.row(0).num_float = 9000; // allows editing
romClass.row(0).num_float = 9000; // error
RomClass romClass2(romTable);
romClass2.row(0).num_float = 9000; // also an error
return 0;
}理想情况下,我想放弃RamClass,只需要一个RomClass,它还具有非const成员函数,该函数将公开表并允许编辑。但是,下面的代码不会产生错误:
RomClass romClass2(romTable);
romClass2.row(0).num_float = 9000; // not an error, but it should be我希望我能用这段代码来做任何改进,同时实现我上面发布的目标,并且尽可能地对用户友好--不允许意外地编辑只读数据。
发布于 2016-03-01 16:13:54
我看到了一些可以帮助您改进代码的东西。
Row和m_table的名字很好,因为它们是描述性的,但是num_float和num_int不是很好。如果变量名称描述它们的重要性而不是它们的格式会更好。类似地,虽然RomClass和RamClass对您这个实现者来说可能很重要,但它们实际上并不是对这些类中实际内容的描述。
assertRomClass的代码当前具有以下成员函数:
const Row& row(std::size_t i) const {
assert(m_size > i);
return m_table[i];
}但是,最好是抛出一个std::out_of_range错误,让调用者找出适当的操作。
)
现在编写代码时,任何访问包含类的内容都可以更改内部Row数据。这可能很方便,但通常只允许通过显式访问器函数访问内部类数据更好。此外,返回成员的副本(如代码中的Row )通常比返回引用更安全。
RomClass和RamClass之间的差异与const变量和非const变量之间的差异完全相同。由于const-ness的概念已经存在,并且得到了标准的显式支持,所以最好简单地使用它,而不是为本质上相同的事情使用两个不同的类。这是近在咫尺的(但不要用它!)是两个类的简单组合,作为一个类:
class Table
{
public:
template <std::size_t N>
explicit constexpr Table(const Row (&table)[N])
: m_size(N),
m_table(const_cast<Row *>(table))
{
}
Table(const Table& other)
: m_size(other.m_size)
{
m_table = new Row[m_size]();
}
std::size_t size() const { return m_size; }
const Row& row(std::size_t i) const {
if (m_size <= i) {
throw std::out_of_range("Table index out of bounds");
}
return m_table[i];
}
Row& row(std::size_t i) {
if (m_size <= i) {
throw std::out_of_range("Table index out of bounds");
}
return m_table[i];
}
private:
std::size_t m_size = 0;
Row* m_table = nullptr;
};我们现在可以如下所示:
static constexpr Row romTable[]
{
Row(19.99F, 2),
Row(2.3F, 4),
Row(), // end element
};
constexpr Table romClass{romTable};
int main()
{
Table ramClass{romClass};
ramClass.row(0).num_float = 9000; // allows editing
// romClass.row(0).num_float = 9000; // error
assert(ramClass.row(0).num_float == 9000);
assert(romClass.row(0).num_float == 19.99F);
const Row myrow = romClass.row(1);
assert(myrow.num_int == 4);
assert(myrow.num_float == 2.3F);
const Table romClass2(romTable);
// romClass2.row(0).num_float = 9000; // also an error
}它没有使用上述所有建议(例如,它仍然返回一个&Row),但它与您想要的非常接近。然而,这有一个重要的问题,那就是没有析构函数,因此这个版本会泄漏内存。修复这个问题有很多种方法,但一个简单的方法是从Table派生一个名为WritableTable的新类。现在一切正常,我们不会泄露记忆:
class Table
{
public:
template <std::size_t N>
explicit constexpr Table(const Row (&table)[N])
: m_size(N),
m_table(const_cast<Row *>(table))
{
}
std::size_t size() const { return m_size; }
const Row& row(std::size_t i) const {
if (m_size <= i) {
throw std::out_of_range("Table index out of bounds");
}
return m_table[i];
}
Row rowCopy(std::size_t i) const {
if (m_size <= i) {
throw std::out_of_range("Table index out of bounds");
}
return m_table[i];
}
protected:
std::size_t m_size;
Row* m_table = nullptr;
};
class WritableTable : public Table
{
public:
WritableTable(const Table& other)
: Table(other)
{
m_table = new Row[m_size]();
for (size_t i=0; i < m_size; ++i) {
m_table[i] = other.rowCopy(i);
}
}
Row& row(std::size_t i) { return m_table[i]; }
~WritableTable() {
delete[] m_table;
}
};
static constexpr Row romTable[]
{
Row(19.99F, 2),
Row(2.3F, 4),
Row(), // end element
};
static constexpr Table romClass{romTable};
int main()
{
WritableTable ramClass{romClass};
ramClass.row(0).num_float = 9000; // allows editing
// romClass.row(0).num_float = 9000; // error
assert(ramClass.row(0).num_float == 9000);
assert(romClass.row(0).num_float == 19.99F);
const Row myrow = romClass.row(1);
assert(myrow.num_int == 4);
assert(myrow.num_float == 2.3F);
const Table romClass2(romTable);
// romClass2.row(0).num_float = 9000; // also an error
WritableTable ramClass2{romClass};
ramClass2.row(0).num_int = 42;
assert(ramClass2.row(0).num_int == 42);
}https://codereview.stackexchange.com/questions/121420
复制相似问题