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

标签 c++ containers return-value encapsulation

我想创建一个类来管理 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/

相关文章:

c++ - 重构 MFC 消息映射以包含完全限定的成员函数指针

c++ - 枚举值的编译器支持

c++ - 确定线程使用的用户和系统时间

node.js - 如何使用 gulp browser-sync 与 2 个不同的 docker 容器?

javascript - Jquery Click函数-获取被点击标签的值

c++ - VSPerf VS2010 和其他分析工具没有获取 pdb

bash - 为什么 "docker run"错误与 "no such file or directory"?

docker - 在 docker 容器中运行 Jenkins 有什么好处

java - 该方法必须返回 boolean(Java) 类型的结果

java - 返回数组与填充数组