我遇到了一个稍微不寻常的问题。考虑以下代码:
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*
成员指向它?使用boost::optional<lexer>
会员?还是我应该只接受这种init
方法?还是我应该在构造函数中进行解析并抛出一个包含所需结果的“期望”?