在不破坏C+中的抽象的情况下,处理对私有映射中存储的值的封装访问的标准方法+; 我想创建一个类来管理C++中的标记语言(如HTML)。我希望我的类保留属性和子标记。问题是,给定封装的容器,如何正确地抽象访问以及返回什么,以便提供一种简单的方法来检查返回的值是否有效
我将包含两个映射的类定义为私有成员(名义上是在不破坏C+中的抽象的情况下,处理对私有映射中存储的值的封装访问的标准方法+; 我想创建一个类来管理C++中的标记语言(如HTML)。我希望我的类保留属性和子标记。问题是,给定封装的容器,如何正确地抽象访问以及返回什么,以便提供一种简单的方法来检查返回的值是否有效,c++,containers,return-value,encapsulation,C++,Containers,Return Value,Encapsulation,我将包含两个映射的类定义为私有成员(名义上是std::map\u children;和std::map\u attr;) 问题是,我不想打破我的抽象,我这样做是为了处理我的C++技能,我想找到合适的方法(或者更干净的方法,或者标准的方法)。 一个基本的解决方案是简单地调用returnmap.find(s);,但是我必须将我的函数的返回类型定义为std::map::const_迭代器,这将破坏抽象。因此我可以取消引用map.find()返回的迭代器,但如果映射中没有中的值,我将取消引用不可取消引用
std::map\u children;
和std::map\u attr;
)
问题是,我不想打破我的抽象,我这样做是为了处理我的C++技能,我想找到合适的方法(或者更干净的方法,或者标准的方法)。
一个基本的解决方案是简单地调用returnmap.find(s);
,但是我必须将我的函数的返回类型定义为std::map::const_迭代器
,这将破坏抽象。因此我可以取消引用map.find()返回的迭代器
,但如果映射中没有中的值,我将取消引用不可取消引用的迭代器(\u 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]+)\"");
使用名称空间std;
类标签{
静态常量regex re_get_name、re_get_属性;
字符串\u名称;
地图属性;
地图儿童;
公众:
标记(常量字符串和toParse){
/*使用正则表达式解析行*/
}
常量字符串&name()常量{
返回_name;
}
标记和添加子项(常量标记和子项){
_儿童。插入(儿童姓名,儿童);
归还*这个;
}
SOMETHING get_child(const字符串和名称)const{
map::const_迭代器val=_children.find(name);
/*在这里做点什么,但是什么*/
归还某物;
}
属性(常量字符串和名称)常量{
map::const_iterator val=_attr.find(name);
/*在这里做点什么,但是什么*/
归还某物;
}
};
const regex Tag::re_get_name(“^std::optional
可以帮助您,但需要一个C++17就绪的标准库,因此在此期间您还可以使用大致相同的boost::optional
,因为AFAIKstd::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::regex re_get_name,re_get_属性;
使用string=std::string;
字符串\u名称;
标准::地图_属性;
性病:地图儿童;
公众:
标记(常量字符串和toParse){
/*使用正则表达式解析行*/
}
常量字符串&name()常量{
返回_name;
}
标记和添加子项(常量标记和子项){
_儿童就业(儿童姓名,儿童);
归还*这个;
}
boost::可选的get_child(const字符串和名称)const{
auto val=_children.find(name);
return val==_children.cend()?boost::optional{}:boost::optional{val->second};
}
boost::可选属性(常量字符串和名称)常量{
自动值=_attr.find(名称);
return val==_attr.cend()?boost::optional{}:boost::optional{val->second};
}
};
正如您所看到的,您基本上只是重新实现了std::map
的容器语义,但也使用了某种内置的解析器逻辑。我强烈反对这种方法,因为解析很快就会变得难看,并且将值生成代码混合到一个可以(即应该)用作值类的容器中会使事情变得复杂更糟的是
我的第一个建议是只声明/使用标记
类/结构作为值类,因此只包含std::maps作为公共成员。将解析函数与标记容器一起放在命名空间中,如果需要,让它们只是函数或不同的类
我的第二个建议很小:不要用\uuu
作为前缀,它是保留的,被认为是不好的样式,但可以将它用作后缀。也不要在类/函数/命名空间块(即全局)之外使用命名空间指令,它在.cpp中是不好的样式,在头/h/.hpp中是极坏的样式
我的第三个建议:使用Boost精神Qi-分析器框架,首先你将声明你的值类,然后通过Boost融合自动填充它们。如果你已经知道EBNF符号,你可以在C++中写EBNF类语法,编译器会通过模板魔术生成解析器。特别是融合有一些问题,但从长远来看,它使事情变得更容易。正则表达式最多只能完成一半的解析逻辑。
看看std::option
。也许我误解了这个问题,但为什么不直接返回map::const_迭代器呢?它是const
迭代器,所以没有封装broken@molbdnilostd::可选guess@user463035818在本例中,是抽象被破坏了。如果我想更改实现而不再使用映射,那么返回类型将更改。@user463035818返回的类型是什么?可选
?我想对于将接收的变量,返回的类型将是相同的Tag::get_child
的返回值。您可以提供一个迭代器
typedef并使用该类型的迭代器,如果用户使用别名,当您将映射更改为其他内容时,他们的代码将不会中断。嗯,不确定,我必须承认我从未获得过什么enc