gpt4 book ai didi

c++ - 在不破坏 C++ 抽象的情况下处理对存储在私有(private)映射中的值的封装访问的标准方法

转载 作者:行者123 更新时间:2023-11-30 03:18:51 25 4
gpt4 key购买 nike

我想创建一个类来管理 C++ 中的标记语言(例如 HTML)。我希望我的类(class)保留属性和子标签。问题是,给定封装的容器,如何正确抽象访问和返回什么,以便提供一种简单的方法来检查返回的值是否有效。

我将包含两个映射的类定义为私有(private)成员(名义上是 std::map<std::string, Tag> _children;std::map<std::string, std::string> _attr; )。我定义了两个函数来填充这些字段,我想定义两个函数来读取访问存储的元素。

问题是,我不想破坏我的抽象,因为我这样做是为了提高我的 c++ 技能,我想找到正确的方法(或更简洁的方法,或标准方法)去做。

一个基本的解决方案是简单地调用 return map.find(s); ,但是我必须将函数的返回类型定义为 std::map<std::string, Tag>::const_iterator ,这会破坏抽象。所以我可以取消引用 map.find() 返回的迭代器,但如果值不在 map 中,我将取消引用不可取消引用的迭代器 ( _children.cend())。

到目前为止我定义了什么:

using namespace std;
class Tag {
static const regex re_get_name, re_get_attributes;
string _name;
map<string,string> _attr;
map<string,Tag> _children;
public:
Tag(const string &toParse) {
/* Parse line using the regex */
}
const string& name() const {
return _name;
}
Tag& add_child(const Tag& child) {
_children.insert(child._name, child);
return *this;
}
SOMETHING get_child(const string& name) const {
map<string,Tag>::const_iterator val = _children.find(name);
/* Do something here, but what ? */
return something;
}
SOMETHING attr(const string& name) const {
map<string, string>::const_iterator val = _attr.find(name);
/* Do something here, but what ? */
return something;
}
};

const regex Tag::re_get_name("^<([^\\s]+)");
const regex Tag::re_get_attributes(" ([^\\s]+) = \"([^\\s]+)\"");

在 C++ 中处理这种情况的正确方法是什么?我应该创建自己的 Tag::const_iterator 吗?类型?如果是这样,怎么办?我是否应该采用更“C”的方法,我只将返回类型定义为 Tag&并返回 NULL如果 map 不包含我的 key ?我是否应该使用静态成员更面向对象 static const Tag NOT_FOUND ,如果该元素不在我的 map 中,则返回对此对象的引用?我也想过抛出异常,但是异常管理在C++中似乎相当繁重且效率低下。

最佳答案

std::optional 可以帮助你,但需要一个 C++17 就绪的标准库,所以与此同时你也可以使用 boost::optional 这是或多或少是一样的,因为 AFAIK std::optional 的设计是基于 boost 的。 (因为 boost 通常是新 C++ 标准提案的来源)

尽管由于您的方法存在普遍问题,我不愿意向您提出建议,但我还是为您写了一个,但请考虑代码后的要点:

#include <string>
#include <regex>
#include <map>
#include <boost/optional.hpp>

class Tag {
static const std::regex re_get_name, re_get_attributes;
using string = std::string;
string _name;
std::map<string,string> _attr;
std::map<string,Tag> _children;
public:
Tag(const string &toParse) {
/* Parse line using the regex */
}
const string& name() const {
return _name;
}
Tag& add_child(const Tag& child) {
_children.emplace(child._name, child);
return *this;
}
boost::optional<Tag> get_child(const string& name) const {
auto val = _children.find(name);

return val == _children.cend() ? boost::optional<Tag>{} : boost::optional<Tag>{val->second};
}
boost::optional<string> attr(const string& name) const {
auto val = _attr.find(name);

return val == _attr.cend() ? boost::optional<string>{} : boost::optional<string>{val->second};
}
};

如您所见,您基本上只是在重新实现 std::map 的容器语义,而且还使用某种内置的解析器逻辑。我强烈反对这种方法,因为解析很快就会变得非常丑陋,并且将值生成代码混合到一个容器中,即应该用作值类会使事情变得更糟。

我的第一个建议是只声明/使用你的 Tag 类/结构作为值类,所以只包含 std::maps 作为公共(public)成员。将您的解析函数与 Tag 容器一起放在命名空间中,并在需要时让它们只是函数或不同的类。

我的第二个建议是小建议:不要以_ 为前缀,它是保留的并且被认为是不好的风格,但您可以将它用作后缀。也不要在类/函数/命名空间 block 之外使用 using 命名空间指令,即全局,它在 .cpp 中是糟糕的样式,在 header /.h/.hpp 中是非常糟糕的样式

我的第三个建议:使用 boost spirit qi 解析器框架,您只需按照我的建议首先声明您的值类,而 qi 会通过 boost fusion 自动填充它们。如果你已经知道 EBNF 表示法,你可以在 C++ 中编写类似 EBNF 的语法,编译器将通过模板魔法生成一个解析器。然而,气,尤其是融合存在一些问题,但从长远来看,它使事情变得容易得多。正则表达式最多只完成一半的解析逻辑。

关于c++ - 在不破坏 C++ 抽象的情况下处理对存储在私有(private)映射中的值的封装访问的标准方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54294044/

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