首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >ROM / RAM表

ROM / RAM表
EN

Code Review用户
提问于 2016-02-29 13:00:36
回答 1查看 344关注 0票数 3

我的任务是创建一个表,该表将存储在ROM (只读)中,并将用于某些算法(将数字转换为字符串)。但是,有时用户希望在算法中使用该表之前先对其进行编辑。因此,用户必须能够将表复制到RAM中,更改它,然后使用它。

考虑到这一点,我提出了以下代码。注意:这是我的代码的一个简化版本,用来说明解决方案。实际上,RomClass有更多的成员。

表示表中的一行的

代码语言:javascript
复制
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中表的类(只读):

代码语言:javascript
复制
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的引用,因此它可以与算法一起使用。

代码语言:javascript
复制
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函数看起来类似于:

代码语言:javascript
复制
void algorithm(const RomClass& rom)
{
    for (auto i = 0; i < rom.size(); ++i)
    {
        // do sth
    }
}

用法:

代码语言:javascript
复制
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成员函数,该函数将公开表并允许编辑。但是,下面的代码不会产生错误:

代码语言:javascript
复制
RomClass romClass2(romTable);
romClass2.row(0).num_float = 9000;  // not an error, but it should be

我希望我能用这段代码来做任何改进,同时实现我上面发布的目标,并且尽可能地对用户友好--不允许意外地编辑只读数据。

EN

回答 1

Code Review用户

发布于 2016-03-01 16:13:54

我看到了一些可以帮助您改进代码的东西。

改进命名

Rowm_table的名字很好,因为它们是描述性的,但是num_floatnum_int不是很好。如果变量名称描述它们的重要性而不是它们的格式会更好。类似地,虽然RomClassRamClass对您这个实现者来说可能很重要,但它们实际上并不是对这些类中实际内容的描述。

抛出异常,而不是使用assert

RomClass的代码当前具有以下成员函数:

代码语言:javascript
复制
const Row& row(std::size_t i) const { 
    assert(m_size > i);  
    return m_table[i]; 
}

但是,最好是抛出一个std::out_of_range错误,让调用者找出适当的操作。

避免了对类内嵌的原始访问(

)

现在编写代码时,任何访问包含类的内容都可以更改内部Row数据。这可能很方便,但通常只允许通过显式访问器函数访问内部类数据更好。此外,返回成员的副本(如代码中的Row )通常比返回引用更安全。

在这两方面都使用相同的类,

RomClassRamClass之间的差异与const变量和非const变量之间的差异完全相同。由于const-ness的概念已经存在,并且得到了标准的显式支持,所以最好简单地使用它,而不是为本质上相同的事情使用两个不同的类。这是近在咫尺的(但不要用它!)是两个类的简单组合,作为一个类:

代码语言:javascript
复制
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;
};

我们现在可以如下所示:

代码语言:javascript
复制
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的新类。现在一切正常,我们不会泄露记忆:

代码语言:javascript
复制
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);

}
票数 1
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/121420

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档