gpt4 book ai didi

C++ 避免向下转型或变体

转载 作者:搜寻专家 更新时间:2023-10-31 01:39:46 24 4
gpt4 key购买 nike

一段时间以来我一直面临设计问题:

  • 我正在将源代码字符串解析为 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/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com