简体   繁体   English

非成员函数如何改进封装

[英]How Non-Member Functions Improve Encapsulation

I read Scott Meyers' article on the subject and quite confused about what he is talking about. 我阅读了Scott Meyers关于这个主题的文章 ,并对他所谈论的内容感到很困惑。 I have 3 questions here. 我这里有3个问题。

Question 1 问题1

To explain in detail, assume I am writing a simple vector<T> class with methods like push_back , insert and operator [] . 为了详细解释,假设我正在使用push_backinsert和operator []等方法编写一个简单的vector<T>类。 If I followed Meyers' algorithm, I would end up with all non-member friend functions. 如果我遵循Meyers的算法,我最终会得到所有非会员朋友的功能。 I will have a vector class with few private members and many non-member friend functions. 我将有一个带有很少私有成员和许多非成员朋友函数的向量类。 Is this what he is talking about? 这是他在说什么?

Question 2 问题2

I am still not understanding how non-member functions improve encapsulation. 我仍然不了解非成员函数如何改进封装。 Consider the code given in Meyers' article. 考虑一下迈耶斯文章中给出的代码。

class Point {
public:
   int getXValue() const; 
   int getYValue() const; 
   void setXValue(int newXValue);
   void setYValue(int newYValue);

private:
  ...                 // whatever...
};

If his algorithm is followed, setXXXX methods should be non-members. 如果遵循他的算法, setXXXX方法应该是非成员。 My question is how that increases encapsulation? 我的问题是如何增加封装? He also says 他还说

We've now seen that a reasonable way to gauge the amount of encapsulation in a class is to count the number of functions that might be broken if the class's implementation changes. 我们现在已经看到,衡量一个类中封装量的合理方法是计算在类的实现发生变化时可能会破坏的函数数量。

Until we keep the method signature intact when class implementation changes, no client code is gonna break and it is well encapsulated, right? 直到我们在类实现发生变化时保持方法签名不变,没有客户端代码会被破坏并且封装得很好,对吧? The same applies for non-member functions as well. 这同样适用于非成员函数。 So what is the advantage non-member function provides? 那么非成员函数提供的优势是什么?

Question 3 问题3

Quoting his algorithm 引用他的算法

else if (f needs type conversions
         on its left-most argument)
   {
   make f a non-member function;
   if (f needs access to non-public
       members of C)
      make f a friend of C;
   }

What he meant by f needs type conversions on its left-most argument ? 他的意思是f需要在最左边的参数上进行类型转换 He also says the following in the article. 他还在文章中说了以下内容。

Furthermore, we now see that the common claim that "friend functions violate encapsulation" is not quite true. 此外,我们现在看到“朋友功能违反封装”的共同主张并不完全正确。 Friends don't violate encapsulation, they just decrease it — in exactly the same manner as a member functions. 朋友不会违反封装,他们只是减少它 - 与成员函数完全相同。

This and the above algorithm are contradictory, right? 这和上面的算法是矛盾的吧?

Question 1 问题1

In this case, following Meyers's algorithm will give you member functions: 在这种情况下,遵循Meyers的算法将为您提供成员函数:

  • Do they need to be virtual? 他们需要虚拟吗? No. 没有。
  • Are they operator<< or operator>> ? 他们是operator<<operator>> No. 没有。
  • Do they need type conversions? 他们需要类型转换吗? No. 没有。
  • Can they be implemented in terms of the public interface? 它们可以在公共接口方面实现吗? No. 没有。
  • So make them members. 所以让他们成员。

His advice is to only make them friends when they really need to be; 他的建议是只在他们真正需要的时候才能成为他们的朋友; to favour non-member non-friends over members over friends. 支持非会员非朋友而不是朋友。

Question 2 问题2

The SetXXXX functions need to access the internal (private) representation of the class, so they can't be non-member non-friends; SetXXXX函数需要访问类的内部(私有)表示,因此它们不能是非成员非朋友; so, Meyers argues, they should be members rather than friends. 所以,迈耶斯认为,他们应该是成员而不是朋友。

The encapsulation comes about by hiding the details of how the class is implemented; 通过隐藏类的实现细节来实现封装; you define a public interface separately from a private implementation. 您可以与私有实现分开定义公共接口。 If you then invent a better implementation, you can change it without changing the public interface, and any code using the class will continue to work. 如果您随后发明了更好的实现,则可以在不更改公共接口的情况下进行更改,并且使用该类的任何代码都将继续工作。 So Meyers's "number of functions which might be broken" counts the member and friend functions (which we can easily track down by looking at the class definition), but not any non-member non-friend functions using the class through its public interface. 因此,Meyers的“可能被破坏的函数数量”计算成员和朋友函数(我们可以通过查看类定义轻松跟踪),但不是通过其公共接口使用该类的任何非成员非朋友函数。

Question 3 问题3

This has been answered . 这已得到回答

The important points to take away from Meyers's advice are: 从迈耶斯的建议中取消的重点是:

  • Design classes to have clean, stable public interfaces separate from their private implementation; 设计类使其具有与其私有实现分离的干净,稳定的公共接口;
  • Only make functions members or friends when they really need to access the implementation; 只有在真正需要访问实现时才能成为函数成员或朋友;
  • Only make functions friends when they can't be members. 只有当他们不能成为会员时才能成为朋友。

The meaning f needs type conversions on it left-most arg is as follows: 意义f需要在其上进行类型转换 - 最左边的arg如下:

consider following senario : 考虑以下senario:

Class Integer 
{  
    private: 
       int num;
     public:
        int getNum( return num;)
        Integer(int n = 0) { num = n;}

    Integer(const Integer &rhs)) { num = rhs.num ;}
    Integer operator * (const Integer &rhs)
    {
         return Integer(num * rhs.num);
    }
}


int main()
{
    Integer i1(5);

    Integer i3 = i1 *  3; // valid 

    Integer i3 = 3 * i1 ; // error     
}

In above code i3 = i1 * 3 is equivalent to this->operator*(3) which is valid since 3 is implicitly converted to Integer. 在上面的代码中, i3 = i1 * 3相当于this->operator*(3) ,它有效,因为3被隐式转换为Integer。

Where as in later i3 = 3 * i1 is equivalent to 3.operator*(i1) , as per rule when u overload operator using member function the invoking object must be of the same class. 如果稍后i3 = 3 * i1相当于3.operator*(i1) ,根据规则当u重载运算符使用成员函数时,调用对象必须是同一个类。 but here its not that. 但这不是那个。

To make Integer i3 = 3 * i1 work one can define non-member function as follow : 要使Integer i3 = 3 * i1工作,可以定义非成员函数如下:

Integer operator * (const Integer &lhs , const Integer &rhs) // non-member function
    {

         return Integer(lhs.getNum() * rhs.getNum());

    }

I think you will get idea from this example..... 我想你会从这个例子中得到一个想法.....

Of the four cases he provides for making functions non-members, the closest that your proposed vector methods would come to is this one: 在他提出的非成员函数的四个案例中,你提出的vector方法最接近的是这个:

else if (f can be implemented via C's
         public interface)
   make f a non-member function;

But you can't implement methods like push_back , insert or operator[] via a public interface. 但是你不能通过公共接口实现push_backinsertoperator[]等方法。 Those are the public interface. 这些公共界面。 It might be possible to implement push_back in terms of insert , but to to a large degree, what public interface are you going to be using for such methods? 有可能在insert方面实现push_back ,但在很大程度上,您将使用哪种公共接口用于此类方法?

Further the cases for giving friendship to non-member functions are really special cases as I see it, operator<< and operator>> , and type conversions, would both require very accurate, and unfiltered data from the class. 此外,为非成员函数提供友谊的情况实际上是特殊情况,因为我看到它, operator<<operator>>和类型转换,都需要来自类的非常准确和未过滤的数据。 These methods are naturally very invasive. 这些方法自然是非常侵入性的。

While I'm not a fan of Dr. Dobbs, or any of the claimed "C++ gurus", I think in this case you might be double guessing your own implementation. 虽然我不是Dobbs博士或任何声称的“C ++大师”的粉丝,但我想在这种情况下你可能会双重猜测你自己的实现。 Scott Meyer's algorithm seems reasonable to me. Scott Meyer的算法对我来说似乎很合理。

Take a good look at the STL algorithms. 仔细看看STL算法。 sort , copy , transform etc operate on iterators and aren't member functions. sortcopytransform等对迭代器进行操作,而不是成员函数。

You're also wrong about his algorithm. 你的算法也错了。 The set and get functions can't be implemented with Point's public interface. 使用Point的公共接口无法实现set和get函数。

Question : 2 问题2

Scott Meyers has also suggested following thing if u remember : 如果你记得,斯科特迈耶斯也提出了以下建议:

--> Keep the class interface complete and minimal. - >保持类接口完整和最小化。

See following scenario : 请参阅以下方案:

class Person {

private: string name;

         unsigned int age;

         long salary;

public:

       void setName(string);// assme the implementation

       void setAge(unsigned int); // assme the implementation

       void setSalary(long sal); // assme the implementation

       void setPersonData()
       {
           setName("Scott");
           setAge(25);
           selSalary(50000);
        }
}

here setPersonData() is member function but ultimately what its doing can also be achieved by making it non - member function like this and it will keep interface of class minimal and does not bloat class with plenty of member function unnecessarily . 这里的setPersonData()是成员函数,但最终它的作用也可以通过使它成为非成员函数来实现,它将使类的接口保持最小,并且不会不必要地充满成员函数的类。

   void setPersonData(Person &p) 
   {           
       p.setName("Scott");
       p.setAge(25);
       p.selSalary(50000);
    }

I suppose the general point is that it is beneficial to always implement things in terms of other things if you can. 我想一般的观点是,如果可以的话,总是用其他东西来实现它是有益的。 Implementing functionality as non-friend free functions, ensures that this functionality doesn't break if you change the class representation. 将功能实现为非朋友自由函数,可确保在更改类表示时此功能不会中断。

In real life, I guess it might have a problem: you might be able to implement something in terms of the public interface with the current implementation, but if there are changes to the class, this might not be possible any more (and you'll need to start declaring things friends). 在现实生活中,我想它可能有一个问题:您可能能够在当前实现的公共接口方面实现某些功能,但如果该类有更改,则可能不再可能(并且您'我需要开始宣布朋友的事情)。 (Eg when it comes to algorithmic optimization, the free function might benefit from some extra cached data, that shouldn't be exposed to the public.) (例如,当涉及到算法优化时,自由函数可能会受益于一些额外的缓存数据,这些数据不应向公众公开。)

So the guideline I'd derive from it: use common sense, but don't be afraid of free functions. 所以我从中得出的指导原则是:使用常识,但不要害怕自由功能。 They don't make your C++ code less object-oriented. 它们不会使您的C ++代码更少面向对象。


Another things is probably an interface consisting entirely of getters and setters. 另一件事可能是一个完全由getter和setter组成的接口。 This hardly encapsulates anything. 这几乎不包含任何东西。

In case of Point in particular, you might get the temptation to store the data as int coords[2] instead, and in this respect the getters and setters might have a meaning (but one might also always consider the ease of use vs ease of implementation). 特别是在Point的情况下,您可能会试图将数据存储为int coords[2] ,而在这方面,getter和setter可能有意义(但是也可能总是考虑使用的易用性和易用性实现)。

But if you move on to more complicated classes, they should do something (some core functionality) other than just giving access to their data. 但是如果你转向更复杂的类,他们应该一些事情(一些核心功能),而不仅仅是访问他们的数据。


When it comes to vector, some of its methods could have been free functions: assign (in terms of clear + insert), at, back, front (in terms of size + operator[] ), empty (in terms of size or begin / end), pop_back (erase + size), push_back (insert + size), end (begin + size), rbegin and rend (begin and end). 当涉及到vector时,它的一些方法可能是自由函数:assign(在clear + insert方面),at,back,front(就size size + operator[] ),empty(就大小或开始而言) / end), pop_back (擦除+大小), push_back (插入+大小),结束(开始+大小),rbegin和rend(开始和结束)。

But if taken rigorously, this could lead to rather confusing interfaces, eg 但如果采取严格措施,这可能会导致相当混乱的界面,例如

 for (vector<T>::iterator it = v.begin(); it != end(v); ++it)

Furthermore, here one would have to consider the capabilities of other containers. 此外,这里必须考虑其他容器的功能。 If std::list cannot implement end as a free function, then std::vector shouldn't either (templates need one uniform pattern for iterating over a container). 如果std :: list不能作为自由函数实现end,那么std :: vector也不应该(模板需要一个统一模式来迭代容器)。

Again, use common sense. 再次,使用常识。

He specifically says "non-member non-friend functions" (emphasis mine). 他特别说“非会员非朋友职能”(强调我的)。 If you would need to make the non-member function a fiend his algorithms says it should be a member function unless it's operator>> or operator<< or needs type conversions on its left-most argument. 如果你需要将非成员函数作为恶魔,他的算法说它应该是一个成员函数,除非它是operator >>或operator <<或者需要在其最左边的参数上进行类型转换。

Until we keep the method signature intact when class implementation changes, no client code is gonna break and it is well encapsulated, right? 直到我们在类实现发生变化时保持方法签名不变,没有客户端代码会被破坏并且封装得很好,对吧? The same applies for non-member functions as well. 这同样适用于非成员函数。 So what is the advantage non-member function provides? 那么非成员函数提供的优势是什么?

Meyers is saying that a class with many methods is less encapsulated than a class with fewer methods, because the implementations of all those internal methods are subject to changing. Meyers说,具有许多方法的类比具有更少方法的类封装更少,因为所有这些内部方法的实现都会发生变化。 If any of the methods could have been non-members, that would reduce the number of methods that could be affected by changes internal to the class. 如果任何方法可能是非成员,那么将减少可能受到类内部更改影响的方法的数量。

What he meant by f needs type conversions on its left-most argument? 他的意思是f需要在最左边的参数上进行类型转换?

I think he's referring to operators, functions that would have an implicit left-most argument if they were member functions. 我认为他指的是运算符,如果它们是成员函数,则会有一个隐含的最左边的参数。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM