重载流插入而不违反信息隐藏?
我正在为项目使用 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] >>温度0; v.number( temp0 );
到处都是,不仅乏味、容易出错、丑陋,而且相当浪费(额外的副本)。
然后我明白了:我尝试将这两个运算符移至 Note
类本身,并将它们声明为 friend
,但编译器(GCC 4.4)不喜欢那:
<块引用>src/note.h:44: 错误:'YAML::Emitter&注意::operator<<(YAML::Emitter&, const Note&)' 必须仅采用一个参数
src/note.h:45: 错误:'void Note::operator>>(const YAML::Node&, Note&)' 必须只接受一个参数
问题: 我如何“正确地” " 为类重载 >>
运算
- 符而不违反信息隐藏原则?
- 没有过度复制?
I'm using yaml-cpp for a project. I want to overload the <<
and >>
operators for some classes, but I'm having an issue grappling with how to "properly" do this. Take the Note
class, for example. It's fairly boring:
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;
};
The <<
operator is easy sauce. In the .h
:
YAML::Emitter& operator << ( YAML::Emitter& out, const Note& v );
And in the .cpp
:
YAML::Emitter& operator << ( YAML::Emitter& out, const Note& v ) {
out << v.number() << v.author() << v.subject() << v.body();
return out;
}
No sweat. Then I go to declare the >>
operator. In the .h
:
void operator >> ( const YAML::Node& node, Note& note );
But in the .cpp
I get:
void operator >> ( const YAML::Node& node, Note& note ) {
node[0] >> ?
node[1] >> ?
node[2] >> ?
node[3] >> ?
return;
}
If I write things like node[0] >> v._number;
then I would need to change the CV-qualifier to make all of the Note
fields public
(which defeats everything I was taught (by professors, books, and experience))) about data hiding.
I feel like doing node[0] >> temp0; v.number( temp0 );
all over the place is not only tedious, error-prone, and ugly, but rather wasteful (what with the extra copies).
Then I got wise: I attempted to move these two operators into the Note
class itself, and declare them as friend
s, but the compiler (GCC 4.4) didn't like that:
src/note.h:44: error: ‘YAML::Emitter& Note::operator<<(YAML::Emitter&, const Note&)’ must take exactly one argument
src/note.h:45: error: ‘void Note::operator>>(const YAML::Node&, Note&)’ must take exactly one argument
Question: How do I "properly" overload the >>
operator for a class
- Without violating the information hiding principle?
- Without excessive copying?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
在不违反封装的情况下执行此操作的典型方法是使运算符>>好友功能。您对友元运算符的声明一定存在语法问题(从错误消息中不清楚到底是什么)。我不使用 YAML,但从您的问题来看,以下是其要点:
友元函数对私有成员具有与成员函数相同的访问权限。
或者,您可以为所有成员数据声明 setter,但友元函数方法更清晰。
The typical way to do this without violating encapsulation is to make the operator>> a friend function. There must have been a syntax problem with your declaration of a friend operator (not clear what exactly from the error message). I don't use YAML, but from your question the following is the jist of it:
A friend function has the same access rights to private members as a member function.
Alternatively, you can declare setters for all member data, but the friend function method is cleaner.
我喜欢使用辅助方法。由于该方法是类的一部分,因此它将具有对所有私有字段的完全访问权限:
然后让
operator>>
转发调用:I like to use a helper method. Since the method is part of the class, it will have full access to all private fields:
and then have
operator>>
just forward the call:您可以在
Note
中定义更多 setter 方法,例如等,然后定义语法糖
>>
因为我不熟悉您的 YAML 命名空间正在使用(我知道
yaml
但我从未在 C++ 中处理过它),但这大致就是您使用普通流的方式(除了void
返回类型; -),我相信它可以轻松适应您的具体需求。You define further setter methods in
Note
, such asetc, and you then define the syntax-sugar
>>
asI'm not familiar with the YAML namespace you're using (I know
yaml
but I've never handled it in C++), but that's roughly how you'd doit with normal streams (apart from thevoid
return types;-), and I'm sure it can be easily adapted to your exact needs.您的类已经有 setter 方法。只需使用临时变量来读取值并使用 setter 方法来配置对象即可:
}
其他一些注释: 具有所有属性的 setter/getter 方法的类很难被封装。您为用户提供了与您的字段实际上是公共字段相同的访问级别(唯一的优点是您可以稍后添加检查,但封装仍然很弱)。
在建议添加采用 YAML 节点的成员方法的解决方案中,这将为类的所有用户添加额外的依赖项。虽然您可以使用前向声明来避免强制它们包含 YAML 标头,但您将无法使用
Note
提取库以在不轻易使用 YAML 的其他项目中使用。资源的潜在浪费可能会非常有限。再说一次,一如既往,首先测量,然后尝试解决问题(如果有问题)。
Your class already has setter methods. Just use temporaries to read the values and use the setter methods to configure the object:
}
Some other comments: A class that has setters/getters for all the attributes is hardly encapsulated. You are giving users the same access level as if your fields were actually public (with the only advantage that you can add checking at a later time, but still, the encapsulation is weak).
On the solutions that suggest adding a member method that takes the YAML node, that will add an extra dependency to all users of your class. While you can use forward declarations to avoid forcing them to include the YAML headers, you will not be able to pull a library with your
Note
to use in a different project that does not use YAML easily.The potential wasteful usage of resources is probably going to be very limited. Then again, as always, first measure and then try to solve problems if you have them.
嗯,这是您可能会考虑的一个想法。您所说的与非朋友、非会员之间的问题 <<功能的一个特点是它涉及到很多tmp声明。您是否考虑过封装这个概念并围绕它构建一个可重用的组件?使用可能如下所示:
input_helper
的职责只是提供模板函数setter()
,该函数返回一个对象,该对象仅读取值并用它调用 setter ,创建必要的临时变量。像这样的代码需要对模板有一定的熟悉,但不会特别困难。现在无法完全清晰地思考——可能是感冒了——或者我可能只能把它打出来。也许有点像这样:其中肯定有各种各样的错误,但它应该给你一个想法。还需要更多的工作来概括。
Well, here's an idea that you might consider. The problem you say you have with the non-friend, non-member << function is that it involves a lot of tmp declarations. Have you considered encapsulating the concept and building a reusable component around it? Use might look something like so:
The responsibility of
input_helper
is simply to supply the template functionsetter()
that returns an object that simply reads the value and calls the setter with it, creating the necessary temporary variable. Code like this would require some intimate familiarity with templates but wouldn't be particularly difficult. Can't think completely straight right now--might be getting a cold--or I'd probably be able to just type it out. Maybe something sort of like so:There's surely all kinds of errors in that but it should give you the idea. Would also require more work to generalize.