访问器方法(getter和setter)在C ++中的约定
关于C ++的访问器方法的几个问题已经被问到,但是没有一个能够满足我对这个问题的好奇心。
我尽可能地避免使用访问器,因为像Stroustrup和其他着名程序员一样,我认为一个类中有许多是OO的坏迹象。 在C ++中,我可以在大多数情况下为类添加更多的责任,或者使用friend关键字来避免它们。 但在某些情况下,您确实需要访问特定的class级成员。
有几种可能性:
1.根本不使用访问器
我们可以公开各自的成员variables。 这在Java中是不可行的,但对于C ++社区来说似乎没有问题。 然而,我有点担心的情况是明确的复制或只读(const)引用的对象应该被返回,这是夸大?
2.使用Java风格的get / set方法
我不确定它是否来自Java,但我的意思是:
int getAmount(); // Returns the amount void setAmount(int amount); // Sets the amount
3.使用客观的C风格get / set方法
这有点奇怪,但显然日益普遍:
int amount(); // Returns the amount void amount(int amount); // Sets the amount
为了这个工作,你将不得不为你的成员variablesfind一个不同的名字。 有些人附加一个下划线,其他人则加上“m_”。 我也不喜欢。
你使用哪种风格,为什么?
从我的观点来看,从维护的angular度来看,有400万行C ++代码(这只是一个项目),我会说:
-
如果成员是不可变的(即
const
)或简单的没有依赖关系的(比如带有成员X和Y的点类),可以不使用getters / setter。 -
如果成员是
private
的,也可以跳过getter / setter。 如果.cpp单元很小,我也把内部pimpl类的成员计算为private
。 -
如果成员是
public
或者protected
(protected
和public
一样坏),非const
,非简单或者依赖,那么使用getters / setter。
作为一个维护人员,我想要有getter / setter的主要原因是因为那时我有一个放置断点/logging/别的东西的地方。
我更喜欢替代方式2,因为这是更可search的(编写可维护代码的关键组件)。
-
我从来不使用这种风格。 因为它可以限制你的类devise的未来,而明确的geters或者setter对于一个好的编译器来说是同样高效的。
当然,实际上,内联显式的getter或者setter在类实现上创build了同样多的依赖关系。 这只是减less语义依赖。 如果你改变它们,你仍然必须重新编译所有东西。
-
这是我使用存取方法时的默认样式。
-
这种风格对我来说太“聪明”了。 我在极less数情况下使用它,但是只有在我真的希望访问者尽可能多地感受像variables那样的情况下。
我确实认为有一些简单的variables可能带有一个构造函数,以确保它们都被初始化为一个合理的东西。 当我这样做的时候,我只是简单地把它变成一个struct
然后把它全部公开。
2)是最好的海事组织,因为它使你的意图最清晰。 set_amount(10)
比amount(10)
更有意义,并且一个很好的副作用允许一个名为amount
的成员。
公共variables通常是一个坏主意,因为没有封装。 假设你需要更新caching或更新variables时刷新窗口? 太糟糕,如果你的variables是公开的。 如果你有一个设置的方法,你可以在那里添加它。
-
如果我们只想表示
pure
数据,这是一种很好的风格。 -
我不喜欢它:),因为
get_/set_
在我们可以在C ++中重载它们时是不必要的。 -
STL使用这种风格,如
std::streamString::str
和std::ios_base::flags
,除非应该避免! 什么时候? 当方法名与其他types的名称冲突时,使用get_/set_
std::string::get_allocator
,比如std::string::get_allocator
因为std::allocator
。
总的来说,我觉得有太多的getter和setter被系统中太多的实体所使用,这不是个好主意。 这只是一个糟糕的devise或封装错误的迹象。
话虽如此,如果这样的devise需要重构,而源代码是可用的,我宁愿使用访客devise模式。 原因是:
一个。 它给了class级一个机会来决定谁允许进入其私人状态
湾 它给了class级一个机会来决定什么访问允许每个对私人状态感兴趣的实体
C。 它明确地通过清晰的类接口来logging这种外部访问
基本的想法是:
a)如果可能的话,重新devise,
b)重构这样的
所有对类的访问都是通过一个众所周知的个人化界面来实现的
应该可以为每个这样的接口configuration某种“做什么”和“不该做什么”,例如,应当允许来自外部实体GOOD的全部访问,不允许来自外部实体BAD的所有访问,并且允许外部实体OK未设置(例如)
-
我不会排除访问者的使用。 可能对于某些POD结构,但是我认为它们是一件好事(某些访问器也可能有其他逻辑)。
-
如果你在你的代码中是一致的,那么这个命名约定并不重要。 如果您使用多个第三方库,则可能会使用不同的命名约定。 所以这是一个品味的问题。
让我告诉你一个额外的可能性,这似乎是最有意义的。
需要阅读和修改
简单地声明variablespublic:
class Worker { public: int wage = 5000; } worker.wage = 8000; cout << worker.wage << endl;
只需要阅读
class Worker { int _wage = 5000; public: inline int wage() { return _wage; } } worker.wage = 8000; // error !! cout << worker.wage() << endl;
这种方法的缺点是,当你想改变访问模式时,你需要改变所有的调用代码(加上括号)。
另外的可能性可能是:
int& amount();
我不确定我会推荐它,但它的优点是不寻常的符号可以阻止用户修改数据。
str.length() = 5; // Ok string is a very bad example :)
有时候,这可能只是一个很好的select:
image(point) = 255;
另一种可能性是,使用function符号来修改对象。
edit::change_amount(obj, val)
这种方式危险/编辑function可以在一个单独的命名空间与自己的文档。 这一个似乎自然与generics编程。
我已经看到类的理想化,而不是整数types来引用有意义的数据。
下面的东西通常不会很好地利用C ++的属性:
struct particle { float mass; float acceleration; float velocity; } p;
为什么? 因为p.mass * p.acceleration的结果是一个浮动,而不是预期的力量。
用于指定目的的类的定义(即使它是一个值,就像前面提到的数量一样)更有意义,并允许我们执行如下操作:
struct amount { int value; amount() : value( 0 ) {} amount( int value0 ) : value( value0 ) {} operator int()& { return value; } operator int()const& { return value; } amount& operator = ( int const newvalue ) { value = newvalue; return *this; } };
您可以通过操作符int隐式地访问该值。 此外:
struct wage { amount balance; operator amount()& { return balance; } operator amount()const& { return balance; } wage& operator = ( amount const& newbalance ) { balance = newbalance; return *this; } };
Getter / Setter用法:
void wage_test() { wage worker; (amount&)worker = 100; // if you like this, can remove = operator worker = amount(105); // an alternative if the first one is too weird int value = (amount)worker; // getting amount is more clear }
这是一个不同的方法,并不意味着它的好坏,而是不同的。