- Java 双重比较
- java - 比较器与 Apache BeanComparator
- Objective-C 完成 block 导致额外的方法调用?
- database - RESTful URI 是否应该公开数据库主键?
一段时间以来我一直面临设计问题:
我正在将源代码字符串解析为 token 对象的一维数组。
根据 token 的类型(文字、符号、标识符),它有一些特定于 token 类型的数据。文字有值,符号有符号类型,标识符有名称。
然后,我通过分析这个 1 维标记数组来构建在该源代码字符串中定义的脚本的抽象表示。语法分析逻辑是在这些 token 对象之外完成的。
我的问题是我需要将所有标记(无论它们的类型)存储到一个数组中,因为它似乎更容易分析,而且我看不到任何其他方法可以做到这一点。这涉及通过创建类层次结构为所有不同的 token 类型提供通用类型:
class token { token_type type; };
class identifier : public token { string name; };
class litteral : public token { value val; };
class symbol : public token( symbol_type sym; };
...或通过创建变体:
class token
{
token_type type;
string name; // Only used when it is an identifier
value val; // Only used when it is a litteral
symbol_type sym; // Only used when it is a symbol
};
类层次结构将按如下方式使用:
// Iterator over token array
for( auto cur_tok : tokens )
{
// Do token-type-specific things depending on its type
if( cur_token->type == E_SYMBOL )
{
switch( ((symbol *) cur_token)->symbol_type )
{
// etc
}
}
}
但是它有几个问题:
基标记类必须知道它的子类,这似乎是错误的。
它涉及根据 token 类型访问特定数据的向下转换,有人告诉我这也是错误的。
变体解决方案将以类似的方式使用,无需向下转换:
for( auto cur_token: tokens )
{
if( cur_token->type == E_SYMBOL )
{
switch( cur_token->symbol_type )
{
// etc
}
}
}
第二个解决方案的问题是它将所有东西混合到一个类中,这对我来说似乎不太干净,因为根据 token 的类型有未使用的变量,并且因为一个类应该代表一个单一的“事物”类型。
您是否有另一种可能性建议设计这个?有人告诉我访客模式,但我无法想象我将如何在我的案例中使用它。
我想保留对数组进行迭代的可能性,因为我可能必须从两个方向迭代,从一个随机位置开始,也许多次。
谢谢。
最佳答案
选项 1:“胖”类型具有一些共享/一些专用字段
为您的“一些特定于 token 类型的数据选择一组可以以特定于 token 类型的方式重新调整用途的数据成员。文字有一个值,符号有一个符号类型,标识符有一个名称”
struct Token
{
enum Type { Literal, Symbol, Identifier } type_;
// fields repurposed per Token-Type
std::string s_; // text of literal or symbol or identifier
// fields specific to one Token-Type
enum Symbol_Id { A, B, C } symbol_id_;
};
这样做的一个问题是共享字段的名称可能过于模糊,因此它们不会对任何给定的 token 类型产生积极的误导,而“特定”字段仍然可以访问,并且在 token 是另一种。
选项 2:受歧视的 union - 最好为您精心包装 boost::variant<>
:
struct Symbol { ... };
struct Identifier { ... };
struct Literal { ... };
typedef boost::variant<Symbol, Identifier, Literal> Token;
std::list<Token> tokens;
参见 tutorial用于数据检索选项。
选项 3:OOD - 经典的面向对象方法:
几乎是你所拥有的,但至关重要的是 Token
类型需要一个虚拟析构函数。
struct Token { virtual ~Token(); };
struct Identifier : Token { string name; };
struct Literal : Token { value val; };
struct Symbol : Token { symbol_type sym; };
std::vector<std::unique_ptr<Token>> tokens_;
tokens_.emplace_back(new Identifier { the_name });
您不需要“类型”字段,因为您可以使用 C++ 的 RTTI 来检查特定的 Token*
处理特定的派生类型:
if (Literal* p = dynamic_cast<Literal>(tokens_[0].get()))
...it's a literal, can use p->val; ...
您担心的是:
•The base token class has to know about it's subclasses, which seems wrong.
不需要给定 RTTI。
•It involves down casting to access specific data depending on the type of a token, which i was told is wrong too.
通常,在 OO 中,创建一个基类 API 来表达整个层次结构可以实现的一组逻辑操作是实用且可取的,但在您的情况下,可能需要一个“胖”接口(interface)(这意味着 - 许多操作 - 如果它们在 API 中 - 会混淆无操作(即什么也不做)或以某种方式(例如返回值,异常)报告许多操作不受支持。例如,获取符号类型分类对非符号没有意义。使其仅在 dynamic_cast
之后可访问比始终可访问但仅有时有意义要好一点,如“选项 1”,因为在强制转换之后有编译时检查用法。
关于C++ 避免向下转型或变体,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30728102/
我正在使用面部跟踪进行 HCI(人机交互)。我正在尝试使用面部控制 PC。 我有 3x3 二维网格按钮。1 2 34 5 67 8 9 假设,当前焦点在按钮 5 上。如果我按向上箭头,则焦点将在 2
我正在为蛇和梯子制作一 block 板,到目前为止,我已经按降序打印了板。但是,我需要以正确的方式打印电路板。 编辑“螺旋下降”意味着 100...91 81...90 80...71 ...
我有一个可以响应式调整大小的菜单,因此每次调整大小时 div (#menuWFhover) 的内容都会重新排列,因此 div 根据窗口大小具有不同的高度。 当我使用 jQuery slideDown/
我们从服务器获取数据并附加 ListView 。我们的问题是,当向上/向下滚动时,它首先显示白屏,然后显示数据。向下/向上滚动时,之前出现的白屏应删除 for (var i=0; i");
我想要一个汉堡图标动态改变颜色的代码,以适应网站的黑色部分/部分和白色。它最初是 3 段白色,js 代码几乎是好的和正确的,但是当它在白色部分时它一直闪烁,并在红色和白色之间闪烁。 js var to
我对点击事件的页脚位置有疑问。我正在使用 bootstrap css,我有一个可折叠元素(bootstrap 中的 Accordion ),当它折叠时它有一个名为 .accordion-toggle.
在此代码段中,使用关键帧和动画并显示无/ block ,div 动画以在悬停时向下滑动。 h1 { padding: 20px; } div { width: 100%; background
我需要对一个 float 进行四舍五入。例如 4.00011 。内置函数 round() 总是在数字 > .5 时向上舍入,在 = 0 val *= 10 ** precision r
我正在尝试就地缩小文件。 我正在用另一个文件的内容替换一个文件的内容,完成后我想确保源文件是否小于目标文件,目标文件是否正确收缩。 (为什么:因为dest文件是一个备份,写入media的开销很大,所以
似乎每当我用一个负整数除以一个正整数时,我都需要它向下舍入 (向 -inf),而不是向 0。但 C# 和 C++ 都向 0 舍入。 所以我想我需要一个 DivideDownward() 方法。我可以用
考虑这个简单的代码: document.addEventListener( 'keypress', function() { console.log( 'press' ); } ); document
有什么方法可以检查 Azure 资源(例如 Azure IoT 中心或事件中心)是否可用。我所说的可用是指它是否已关闭/工作/正常/启动?我是 Azure 的新手,如果有人能提供一些启发,那就太好了。
有什么方法可以检查 Azure 资源(例如 Azure IoT 中心或事件中心)是否可用。我所说的可用是指它是否已关闭/工作/正常/启动?我是 Azure 的新手,如果有人能提供一些启发,那就太好了。
我见过几个recyclerview的无限滚动示例,但它们都是向下滚动的。 我想要的是一次又一次地加载我的列表,但无论用户是从上面还是从下面滚动。 正常列表 向上滚动 向下滚动 这样可以通过无限滚动一遍
我正在使用带有 View 的 drupal 7,并且我正在使用我发现的向上/向下文本幻灯片的 jquery 脚本。它有效,但是当我尝试将它与带有 ajax 的 View 公开过滤器一起使用时,它似乎不
*场景 我希望制作一个类似于本网站上的 jquery 效果,它位于主 Flash 添加的右侧: http://www.commbank.com.au/ *问题 我已经开始了,但是用很多方法遇到了一些障
互联网。如果这与其他人没有什么关系,请原谅我,但我会将其留在这里,以防这是一个有效的问题。 我正在尝试创建一个文本区域字段,其中用户每次按下键(a-z),都会触发背景颜色更改(在数组中列出)。我一直在
我正在创建一个网络前端来控制一个小型机器人。 Ajax 调用将在 keydown 上进行,以启动机器人,并在 keyup 上进行停止。 我的问题是,当按下某个键时,keyup、keydown 和 ke
我在内容容器中有两个 div,一个向左浮动,另一个向右浮动。我正在使用屏幕的整个宽度。左 div 宽度为 1290px,右 div 宽度为 625px。有时,在加载页面时,滚动条会更改可用屏幕宽度的宽
请看这个UI sketch图片,我在某个站点的侧边栏(黑框)中有这个 div,当我向下滚动或向上滚动时,我不希望它隐藏...我希望它在我向下滚动和移动时自行向下移动当我向上滚动时向上滚动,这样它就永远
我是一名优秀的程序员,十分优秀!