c++ - 非友元、非成员函数增加封装?

标签 c++ encapsulation member non-member-functions

文章中How Non-Member Functions Improve Encapsulation , Scott Meyers 认为没有办法阻止非成员函数“发生”。

Syntax Issues

If you're like many people with whom I've discussed this issue, you're likely to have reservations about the syntactic implications of my advice that non-friend non-member functions should be preferred to member functions, even if you buy my argument about encapsulation. For example, suppose a class Wombat supports the functionality of both eating and sleeping. Further suppose that the eating functionality must be implemented as a member function, but the sleeping functionality could be implemented as a member or as a non-friend non-member function. If you follow my advice from above, you'd declare things like this:

class Wombat {
public:
   void eat(double tonsToEat);
   void sleep(double hoursToSnooze);
};

w.eat(.564);
w.sleep(2.57);

Ah, the uniformity of it all! But this uniformity is misleading, because there are more functions in the world than are dreamt of by your philosophy.

To put it bluntly, non-member functions happen. Let us continue with the Wombat example. Suppose you write software to model these fetching creatures, and imagine that one of the things you frequently need your Wombats to do is sleep for precisely half an hour. Clearly, you could litter your code with calls to w.sleep(.5), but that would be a lot of .5s to type, and at any rate, what if that magic value were to change? There are a number of ways to deal with this issue, but perhaps the simplest is to define a function that encapsulates the details of what you want to do. Assuming you're not the author of Wombat, the function will necessarily have to be a non-member, and you'll have to call it as such:

void nap(Wombat& w) { w.sleep(.5); }

Wombat w;    
nap(w);

And there you have it, your dreaded syntactic inconsistency. When you want to feed your wombats, you make member function calls, but when you want them to nap, you make non-member calls.

If you reflect a bit and are honest with yourself, you'll admit that you have this alleged inconsistency with all the nontrivial classes you use, because no class has every function desired by every client. Every client adds at least a few convenience functions of their own, and these functions are always non-members. C++ programers are used to this, and they think nothing of it. Some calls use member syntax, and some use non-member syntax. People just look up which syntax is appropriate for the functions they want to call, then they call them. Life goes on. It goes on especially in the STL portion of the Standard C++ library, where some algorithms are member functions (e.g., size), some are non-member functions (e.g., unique), and some are both (e.g., find). Nobody blinks. Not even you.

我真的无法理解他在粗体/斜体 句子中所说的话。为什么必须以非成员身份实现?为什么不直接从 Wombat 类继承您自己的 MyWombat 类,并使 nap() 函数成为 MyWombat 的成员?

我刚开始使用 C++,但在 Java 中我可能会这样做。这不是用 C++ 的方式吗?如果不是,为什么会这样?

最佳答案

理论上,您可以这样做,但您真的不想这样做。让我们考虑一下您为什么不想这样做(目前,在原始上下文中——C++98/03,并忽略 C++11 和更新版本中的添加)。

首先,这意味着基本上所有的类都必须编写为充当基类——但对于某些类来说,这只是一个糟糕的想法,甚至可能直接违背基本意图(例如,某些旨在实现享元模式)。

其次,它会使大多数继承变得毫无意义。举一个明显的例子,C++ 中的许多类都支持 I/O。就目前而言,惯用的方法是重载 operator<<operator>>作为免费功能。现在,iostream 的目的是表示至少模糊地类似于文件的东西——我们可以向其中写入数据和/或从中读取数据的东西。如果我们通过继承来支持 I/O,这也意味着任何可以读取/写入任何类似文件的东西。

这根本没有意义。 iostream 至少代表一些类似文件的东西,而不是您可能想要从文件读取或写入文件的所有类型的对象。

更糟糕的是,它会使几乎所有编译器的类型检查变得几乎毫无意义。举个例子,写一个 distance对象变成 person object 没有任何意义——但如果它们都通过派生自 iostream 来支持 I/O,那么编译器就没有办法将其与真正有意义的对象区分开来。

不幸的是,这只是冰山一角。当您从基类继承时,您继承了该基类的限制。例如,如果您使用的基类不支持复制赋值或复制构造,则派生类的对象也不会/不能。

继续前面的例子,这意味着如果你想在一个对象上做 I/O,你不能支持该类型对象的复制构造或复制赋值。

反过来,这意味着支持 I/O 的对象将与支持放入集合中的对象分离(即,集合需要 iostream 禁止的功能)。

底线:我们几乎立即陷入了一个完全无法管理的困惑局面,在这种情况下,我们的任何继承都不再具有任何实际意义,编译器的类型检查将变得几乎完全无用。

关于c++ - 非友元、非成员函数增加封装?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28799225/

相关文章:

c++ - 反向打印数字

c++ - 直接展示视频捕捉性能

shared-libraries - Dart:Web 应用程序的库和导出

c - 验证生成的封装库

c++ - C++如何识别使用哪个函数(类成员函数指针相关)

c++ - C2248 : Cannot access private member declared in class

c++ - 应用于基类列表的覆盖函数

c++ - 如何从 C++ 生成均匀分布在 0 和 1 之间的随机 double ?

c++ - std::size_t 与 size_type 作为参数和函数返回类型

C++ 将方法和字段带入外部范围,无需显式引用