非成员非朋友函数vs私有函数 [英] Non-member non-friend functions vs private functions

查看:128
本文介绍了非成员非朋友函数vs私有函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Herb Sutter说,在C ++中编写方法的最面向对象的方法是使用非成员非朋友函数。这是否意味着我应该采取私人方法,并把它们变成非成员非朋友的功能?这些方法可能需要的任何成员变量都可以作为参数传递。



示例(before):

  class Number {
public:
Number(int nNumber):m_nNumber(nNumber){}
int CalculateDifference(int nNumber){return minus(nNumber) ; }
private:
int minus(int nNumber){return m_nNumber - nNumber; }
int m_nNumber;
};

示例(后):

  int minus(int nLhsNumber,int nRhsNumber){return nLhsNumber  -  nRhsNumber; } 
class Number {
public:
Number(int nNumber):m_nNumber(nNumber){}
int CalculateDifference(int nNumber){return minus(m_nNumber,nNumber); }
private:
int m_nNumber;
};

我在正确的轨道上吗?是否应将所有私有方法移至非成员非朋友函数?

解决方案

我相信自由功能并同意Sutter的观点,相反方向。这不是你应该有你的公共方法依赖于自由函数而不是私人方法,而是你可以通过使用提供的公共接口在类外部构建一个更丰富的接口的自由功能。



也就是说,您不会将您的私有推送到类之外,而是将公共接口减少到最低限度,从而允许您构建剩余的功能最小可能的耦合:只使用公共接口。



在你的例子中,我将移动到类外是CalculateDifference方法,如果它可以有效地表示的其他操作。

  class Number {//小的简单接口:常量数据的访问器,构造函数
public:
explicit Number(int nNumber):m_nNumber(nNumber){}
int value()const {return m_nNumber; }
private:
int m_nNumber;
};
数字运算符+(数字const& lhs,数字const& rhs)//添加到接口
{
return Number(lhs.value()+ rhs.value ;
}
数字operator-(数字const& lhs,数字const& rhs)//向接口添加减法
{
return Number(lhs.value rhs.value());
}

优点是如果你决定重新定义你的Number内部你可以做这么多简单的类),只要你保持你的公共接口常量,那么所有其他功能将开箱即用。内部实现细节不会强制您重新定义所有其他方法。



难的部分(不是上面的简单示例)确定什么是最小的接口提供。文章( GotW#84 ),从这里的上一个问题引用是一个很好的例子。如果你详细阅读它,你会发现你可以大大减少std :: basic_string中的方法的数量,同时保持相同的功能和性能。计数将从103个成员职能下降到只有32个成员。这意味着类中的实现更改将仅影响32个成员,并且由于保留了接口,将不必更改可以实现32个成员方面的其余功能的71个自由函数。 p>

这是重要的一点:它是更封装,因为你正在限制实现更改对代码的影响。



移出原来的问题,下面是一个简单的例子,说明如何使用自由函数改进类的更改的局部性。假设一个具有真正复杂加法运算的复杂类。你可以去实现所有的操作符重写作为成员函数,或者你可以轻松和有效地实现其中的一些内部和提供其余的自由功能:

  class ReallyComplex 
{
public:
ReallyComplex& operator + =(ReallyComplex const& rhs);
};
ReallyComplex operator +(ReallyComplex const& lhs,ReallyComplex const& rhs)
{
ReallyComplex tmp(lhs);
tmp + = rhs;
return tmp;
}

很容易看出,无论原始 operator + = 执行其任务,自由运算符+ 正确执行其职责。现在,对类的任何和所有更改, operator + = 将必须更新,但外部运算符+ 在其生命的其余部分不受影响。



上面的代码是一个常见的模式,而通常不是通过常量引用接收 lhs 临时对象里面,它可以改变,以便参数本身是一个值副本,帮助编译器进行一些优化:

  ReallyComplex运算符+(ReallyComplex lhs,ReallyComplex const& rhs)
{
lhs + = rhs;
return lhs;
}


Herb Sutter has said that the most object oriented way to write methods in C++ is using non-member non-friend functions. Should that mean that I should take private methods and turn them into non-member non-friend functions? Any member variables that these methods may need can be passed in as parameters.

Example (before):

class Number {
 public:
  Number( int nNumber ) : m_nNumber( nNumber ) {}
  int CalculateDifference( int nNumber ) { return minus( nNumber ); }
 private:
  int minus( int nNumber ) { return m_nNumber - nNumber; }
  int m_nNumber;
};

Example (after):

int minus( int nLhsNumber, int nRhsNumber ) { return nLhsNumber - nRhsNumber; }
class Number {
 public:
  Number( int nNumber ) : m_nNumber( nNumber ) {}
  int CalculateDifference( int nNumber ) { return minus( m_nNumber, nNumber ); }
 private:
  int m_nNumber;
};

Am I on the right track? Should all private methods be moved to non-member non-friend functions? What should be rules that would tell you otherwise?

解决方案

I believe in free functions and agree with Sutter, but my understanding is in the opposite direction. It is not that you should have your public methods depend on free functions instead of private methods, but rather that you can build a richer interface outside of the class with free functions by using the provided public interface.

That is, you don't push your privates outside of the class, but rather reduce the public interface to the minimum that allows you to build the rest of the functionality with the least possible coupling: only using the public interface.

In your example, what I would move outside of the class is the CalculateDifference method if it can be represented effectively in terms of other operations.

class Number { // small simple interface: accessor to constant data, constructor
public:
  explicit Number( int nNumber ) : m_nNumber( nNumber ) {}
  int value() const { return m_nNumber; }
private:
  int m_nNumber;
};
Number operator+( Number const & lhs, Number const & rhs ) // Add addition to the interface
{
   return Number( lhs.value() + rhs.value() );
}
Number operator-( Number const & lhs, Number const & rhs ) // Add subtraction to the interface
{
   return Number( lhs.value() - rhs.value() );
}

The advantage is that if you decide to redefine your Number internals (there is not that much that you can do with such a simple class), as long as you keep your public interface constant then all other functions will work out of the box. Internal implementation details will not force you to redefine all the other methods.

The hard part (not in the simplistic example above) is determining what is the least interface that you must provide. The article (GotW#84), referenced from a previous question here is a great example. If you read it in detail you will find that you can greatly reduce the number of methods in std::basic_string while maintaining the same functionality and performance. The count would come down from 103 member functions to only 32 members. That means that implementation changes in the class will affect only 32 instead of 103 members, and as the interface is kept the 71 free functions that can implement the rest of the functionality in terms of the 32 members will not have to be changed.

That is the important point: it is more encapsulated as you are limiting the impact of implementation changes on the code.

Moving out of the original question, here is a simple example of how using free functions improve the locality of changes to the class. Assume a complex class with really complex addition operation. You could go for it and implement all operator overrides as member functions, or you can just as easily and effectively implement only some of them internally and provide the rest as free functions:

class ReallyComplex
{
public:
   ReallyComplex& operator+=( ReallyComplex const & rhs );
};
ReallyComplex operator+( ReallyComplex const & lhs, ReallyComplex const & rhs )
{
   ReallyComplex tmp( lhs );
   tmp += rhs;
   return tmp;
}

It can be easily seen that no matter how the original operator+= performs its task, the free operator+ performs its duty correctly. Now, with any and all changes to the class, operator+= will have to be updated, but the external operator+ will be untouched for the rest of its life.

The code above is a common pattern, while usually instead of receiving the lhs operand by constant reference and creating a temporary object inside, it can be changed so that the parameter is itself a value copy, helping the compiler with some optimizations:

ReallyComplex operator+( ReallyComplex lhs, ReallyComplex const & rhs )
{
   lhs += rhs;
   return lhs;
}

这篇关于非成员非朋友函数vs私有函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆