我遇到了一个稍微不寻常的问题。考虑以下代码:
class parser
{
lexer lex;
public:
node_ptr parse(const std::string& expression)
{
lex.init(expression.begin(), expression.end());
// ...
// call some helper methods
// return the result
}
private:
// lots of small helper methods, many of them accessing lex
};
parse 方法使用 init
初始化词法分析器。方法。在此之前,词法分析器处于无法使用的“默认”状态。通常,应该在构造过程中初始化一个成员,所以我为什么不简单地这样做:
class parser
{
lexer lex;
public:
parser(const std::string& expr) : lex(expr.begin(), expr.end()) {}
node_ptr parse()
{
// call some helper methods
// return the result
}
// ...
};
首先,这意味着客户端可以多次调用 parse 方法,这没有多大意义。
其次,更重要的是,它很容易导致严重的生命周期问题:
parser my_parser("1 * 2 + 3 * 4");
auto root = my_parser.parse();
在上面的代码中,词法分析器将使用一个临时字符串对象进行初始化,该对象在行尾不再存在,因此调用 parse
下一行中的方法将调用未定义的行为。
由于这两个原因,我真的想用同一个方法进行初始化和解析。不幸的是,我无法在构造函数中执行此操作,因为我需要返回结果,而构造函数无法返回结果。
从技术上讲,可以在parse
内部构建词法分析器。方法并在之后销毁它(如果我还相应地更改了构造函数和析构函数):
class parser
{
static std::string dummy;
lexer lex;
public:
parser() : lex(dummy.begin(), dummy.end())
{
lex.~lexer();
}
node_ptr parse(const std::string& expression)
{
new(&lex) lexer(expression.begin(), expression.end());
// call some helper methods
lex.~lexer();
// return the result
}
~parser()
{
new(&lex) lexer(dummy.begin(), dummy.end());
}
// ...
};
但这是迄今为止我很长一段时间以来编写的最丑陋的代码。它也不是异常安全的;如果辅助方法抛出异常怎么办?事实上,这正是遇到解析错误时发生的情况。
那么我该如何解决这个问题呢?在 parse
中使用本地词法分析器并有一个 lexer*
成员(member)指点一下吗?使用boost::optional<lexer>
成员?或者我应该忍受 init
方法?或者我应该在构造函数中进行解析并抛出一个包含所需结果的“期望”?
最佳答案
我绝对不会做你的第二个例子。更好的方法是在 Parse()
中构造 lexer
,并存储指针或 boost::optional
。但如果您想允许这样做,那么您的辅助函数必须在继续之前检查词法分析器是否有效。我觉得很乱。
更好的是让 Parse
成为一个独立的函数。我认为这对调用者来说更明智,并解决您的问题:
void parser_helper(lexer& lex)
{
...
}
node_ptr Parse(const std::string& inp)
{
lexer lex(inp);
...
parser_helper(lex);
...
return ret;
}
或者如果你有更多的状态可以传递......
class parser_helper
{
lexer lex;
... other state here
public:
parser_helper(const std::string& inp) :
lex(inp)
{
}
... helper functions here.
void helper_function() { }
}
node_ptr Parse(const std::string& inp)
{
parser_helper helper(inp);
...
helper.helper_function();
...
return ret;
}
无论哪种方式,词法分析器都应该只是 Parse
函数中的一个自动变量。
这个想法是调用者期望的接口(interface)只是一个函数。不需要让调用者处理一个类,只是因为 Parse
的内部有状态/辅助函数。
关于c++ - 将数据成员的生命周期限制为一种方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11455872/