访问器方法(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或者protectedprotectedpublic一样坏),非const ,非简单或者依赖,那么使用getters / setter。

作为一个维护人员,我想要有getter / setter的主要原因是因为那时我有一个放置断点/logging/别的东西的地方。

我更喜欢替代方式2,因为这是更可search的(编写可维护代码的关键组件)。

  1. 我从来不使用这种风格。 因为它可以限制你的类devise的未来,而明确的geters或者setter对于一个好的编译器来说是同样高效的。

    当然,实际上,内联显式的getter或者setter在类实现上创build了同样多的依赖关系。 这只是减less语义依赖。 如果你改变它们,你仍然必须重新编译所有东西。

  2. 这是我使用存取方法时的默认样式。

  3. 这种风格对我来说太“聪明”了。 我在极less数情况下使用它,但是只有在我真的希望访问者尽可能多地感受像variables那样的情况下。

我确实认为有一些简单的variables可能带有一个构造函数,以确保它们都被初始化为一个合理的东西。 当我这样做的时候,我只是简单地把它变成一个struct然后把它全部公开。

2)是最好的海事组织,因为它使你的意图最清晰。 set_amount(10)amount(10)更有意义,并且一个很好的副作用允许一个名为amount的成员。

公共variables通常是一个坏主意,因为没有封装。 假设你需要更新caching或更新variables时刷新窗口? 太糟糕,如果你的variables是公开的。 如果你有一个设置的方法,你可以在那里添加它。

  1. 如果我们只想表示pure数据,这是一种很好的风格。

  2. 我不喜欢它:),因为get_/set_在我们可以在C ++中重载它们时是不必要的。

  3. STL使用这种风格,如std::streamString::strstd::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)重构这样的

  1. 所有对类的访问都是通过一个众所周知的个人化界面来实现的

  2. 应该可以为每个这样的接口configuration某种“做什么”和“不该做什么”,例如,应当允许来自外部实体GOOD的全部访问,不允许来自外部实体BAD的所有访问,并且允许外部实体OK未设置(例如)

  1. 我不会排除访问者的使用。 可能对于某些POD结构,但是我认为它们是一件好事(某些访问器也可能有其他逻辑)。

  2. 如果你在你的代码中是一致的,那么这个命名约定并不重要。 如果您使用多个第三方库,则可能会使用不同的命名约定。 所以这是一个品味的问题。

让我告诉你一个额外的可能性,这似乎是最有意义的。

需要阅读和修改

简单地声明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 } 

这是一个不同的方法,并不意味着它的好坏,而是不同的。

Interesting Posts