重载流插入而不违反信息隐藏?
-
24-10-2019 - |
题
我在用着 yaml-cpp 对于一个项目。我想要超载 <<
和 >>
某些类的运算符,但我遇到了如何“正确”执行此操作的问题。采取 Note
类,例如。这相当无聊:
class Note {
public:
// constructors
Note( void );
~Note( void );
// public accessor methods
void number( const unsigned long& number ) { _number = number; }
unsigned long number( void ) const { return _number; }
void author( const unsigned long& author ) { _author = author; }
unsigned long author( void ) const { return _author; }
void subject( const std::string& subject ) { _subject = subject; }
std::string subject( void ) const { return _subject; }
void body( const std::string& body ) { _body = body; }
std::string body( void ) const { return _body; }
private:
unsigned long _number;
unsigned long _author;
std::string _subject;
std::string _body;
};
这 <<
算简单酱。在里面 .h
:
YAML::Emitter& operator << ( YAML::Emitter& out, const Note& v );
并且在 .cpp
:
YAML::Emitter& operator << ( YAML::Emitter& out, const Note& v ) {
out << v.number() << v.author() << v.subject() << v.body();
return out;
}
没有汗水。然后我去声明 >>
操作员。在里面 .h
:
void operator >> ( const YAML::Node& node, Note& note );
但在 .cpp
我得到:
void operator >> ( const YAML::Node& node, Note& note ) {
node[0] >> ?
node[1] >> ?
node[2] >> ?
node[3] >> ?
return;
}
如果我写类似的东西 node[0] >> v._number;
那么我需要更改 CV 限定符以使所有 Note
领域 public
(这打败了我所学到的一切(通过教授、书籍和经验))))关于数据隐藏。
我想做 node[0] >> temp0; v.number( temp0 );
到处都是,不仅乏味、容易出错、丑陋,而且相当浪费(额外的副本)。
然后我就聪明了:我试图将这两个操作员移至 Note
类本身,并将它们声明为 friend
s,但编译器(GCC 4.4)不喜欢这样:
src/note.h:44:错误:‘YAML::Emitter& Note::operator<<(YAML::Emitter&, const Note&)’ 必须仅采用一个参数
src/note.h:45:错误:‘void Note::operator>>(const YAML::Node&, Note&)’ 必须只接受一个参数
问题: 我如何“正确”超载 >>
类的运算符
- 不违反信息隐藏原则?
- 没有过度复制?
解决方案
在不违反封装的情况下执行此操作的典型方法是使操作员>>成为朋友功能。您对朋友操作员的声明一定存在语法问题(不清楚错误消息的确切内容)。我不使用YAML,但是从您的问题来看,以下是它的JIST:
class Note{
...
friend void operator >> ( const YAML::Node& node, Note& note );
....
};
void operator >> ( const YAML::Node& node, Note& note ){
node[0] >> note._number;
node[1] >> note._author;
node[2] >> note._subject;
node[3] >> note._body;
}
朋友功能具有与会员功能相同的私人会员访问权限。
另外,您可以为所有成员数据声明设置器,但是朋友函数方法更清洁。
其他提示
我喜欢使用助手方法。由于该方法是课程的一部分,因此它将完全访问所有私人字段:
class Note {
public:
void read(const YAML::Node& node)
{
node >> ...;
}
};
然后有 operator>>
只需转发电话:
const YAML::Node &operator >> ( const YAML::Node& node, Note& note ) {
note.read(node);
return node;
}
您在 Note
, , 如
void number(YAML::Immitter& e) { e>>_number; }
等,然后您定义语法 - 糖 >>
作为
void operator >> ( YAML::Immitter& e, Note& note ) {
note.number(e);
note.author(e);
note.subject(e);
note.body(e);
}
我不熟悉您正在使用的YAML名称空间(我知道 yaml
但是我从未在C ++中处理),但这大致是您使用普通流的方式(除了 void
返回类型;-),我敢肯定它可以很容易地适应您的确切需求。
您的班级已经有二传方法。只需使用临时性读取值并使用setter方法来配置对象:
void operator >> ( const YAML::Emitter& node, Note& note ) {
unsigned long number;
unsigned long author;
// ...
node[0] >> number;
node[1] >> author;
// ... everything properly read, edit the node:
node.number(number);
node.author(author);
// ...
return;
}
其他一些评论:几乎没有封装的所有属性的类别/获取器的类。您正在为用户提供相同的访问级别,就像您的字段实际上是公开的(唯一的优势可以在以后添加检查,但是封装仍然很弱)。
在建议添加一个YAML节点的成员方法的解决方案上,该方法将为您的类的所有用户添加额外的依赖性。虽然您可以使用前瞻性声明避免强迫它们包括YAML标题,但您将无法与您的 Note
用于不容易使用YAML的其他项目。
潜力 浪费 资源的使用可能会非常有限。再说一次,与往常一样,首先要测量,然后尝试解决问题。
嗯,这是一个你可以考虑的想法。您所说的非友元、非成员 << 函数的问题是它涉及大量 tmp 声明。您是否考虑过封装这个概念并围绕它构建一个可重用的组件?使用可能看起来像这样:
inputter& operator >> (inputter& in, my_type & obj)
{
input_helper<my_type> helper(obj);
in >> helper.setter(&my_type::number);
in >> helper.setter(&my_type::subject);
// etc
}
的责任 input_helper
只是提供模板函数 setter()
返回一个对象,该对象只需读取值并用它调用 setter,创建必要的临时变量。像这样的代码需要对模板有一定的熟悉,但不会特别困难。现在无法完全清晰地思考——可能是感冒了——或者我可能只能把它打出来。也许有点像这样:
template < typename T >
struct input_helper
{
input_helper(T & t) : obj(t) {}
template < typename V >
struct streamer
{
streamer(T & t, void (T::*f)(V const&)) : obj(t), fun(f) {}
template < typename Stream >
Stream& read_from(Stream & str) const // yeah, that's right...const; you'll be using a temporary.
{
V v;
str >> v;
obj.(*fun)(v);
return str;
}
private: // you know the drill...
}
template < typename V >
streamer setter(void (T::*fun)(V const&))
{
return streamer(obj, fun);
}
private:
T & obj;
};
// etc... operator >> (blah blah) { return setter.read_from(stream); }
其中肯定存在各种各样的错误,但它应该给你一个想法。还需要更多的工作来概括。