为什么在接口中定义私有成员/方法?
Why are private members/methods defined in the interface?
我一直对大多数OOP语言(或者更确切地说,c++)让您在接口中定义私有方法/成员(这里的接口指的是类声明-似乎我很困惑)这一事实感到困惑。这不是显示了类的实现细节,违背了封装的思想吗?
是否有一个很好的理由,我一直错过了?
对于c++来说,这是一个实现问题。
c++编译器必须能够在只看到类声明而不看到实现的情况下生成使用类的代码。编译器需要的一个非常重要的东西是类实例的大小,因为c++通过嵌入而不是存储对单独对象的引用来处理对象中的子对象。为了能够构建一个对象(例如struct X { Y y; Z z; }
),必须事先知道所有子对象(例如Y
和Z
)的大小。
这个问题的一个解决方法是使用"pimpl"模式(也被称为"编译器防火墙"模式),它允许你对类的用户隐藏所有的内部细节。不幸的是,这带来了一些额外的运行时成本,但大多数时候这是可以忽略不计的。使用这种方法,公共对象将始终具有指针的大小,并且实例中的所有数据将使用额外的间接访问…优点是你可以添加私有数据成员,并且类的用户不需要重新编译(如果你的类是在DLL中,这甚至可以保持二进制兼容性)。
在实现部分只声明私有方法(没有数据)是可能的,而不会给编译器增加任何复杂性,但是c++设计者认为最好为一个类保留一个单独的声明。
实际上,在许多实现中,即使只是添加一个私有方法也可能影响类实例的大小(例如,如果私有方法是类中唯一的虚方法)。
c++是我所知道的唯一这样做的语言;值得注意的是,有几种函数式语言严格区分了公共接口和私有接口的声明。注意c++在声明和定义之间是有区别的:你只需要在接口中声明私有函数,而不是定义它们。
对于成员变量和虚函数,有一个简单的技术原因:它们影响类的物理布局。由于这个布局需要在翻译单元之间是相同的,所以所有单元都需要知道这个布局——从而知道类的物理组成。
我怀疑你还需要在c++中公开声明非虚函数,只是为了保持一致:否则,你必须在不同的地方声明不同的函数,这取决于它们是公共的还是/或虚的。
您可以通过使用句柄-体习惯用法(也以其不那么吸引人的名称"PIMPL"而闻名)来绕过此限制,但是这给解决方案增加了其自身的复杂性。
在c++的特殊情况下,一个原因是为了生成虚指针表,需要声明所有虚函数——无论是公有的还是私有的。可能同样的原因也适用于其他语言。
这并不违背封装的思想,私有方法/成员不是PUBLIC接口的一部分,因此数据仍然是封装的。此外,公共/私有修饰符只强制可见性。
接口一词在c++中的用法有两种含义:面向对象的接口和类型声明。
OOP接口用于封装和多态。在c++中,它通常用纯抽象类实现。PIMPL习语也用于封装。在这两种情况下,使用者都只显示类型的公共成员,并通过间接层访问私有实现。Java和c#都支持显式接口,并且都将其成员限制为public访问。
由于c++的链接模型,在c++中需要使用类型声明。从OOP的意义上说,类型的声明不是它的接口,但是由于必须在使用之前包含类型的声明,因此解耦实现变得更加可取。为了实现这一点,我们使用如上所述的OOP接口。如果只有c++支持的模块,就不需要在类型声明中隐藏私有实现细节。模块支持被提议用于c++ 11,但由于时间限制,将被包含在未来的TR中。
在c++中有很多技巧可以解决这个问题。
可以使用工厂模式向用户隐藏私有成员和方法。您需要的是创建一个abstract class
作为您的API或接口,然后在子类中实现您的类并在那里声明所有私有成员,这样您的用户就不会看到您的私有成员和实现。
你是怎么得出这样的结论的?我发现没有任何OOP语言能让我这样做。对于c++,人们在接口中定义方法的原因是为了内联的目的。对于成员,那是因为你不能在其他地方定义它,它适用于所有可见性,而不仅仅是私有的。
- 从私有成员变量的成员方法返回unique_ptr
- 如何将类成员方法的参数列表自动填充写入可变参数?
- 如何对绑定的成员方法进行typedef,然后将该类型用作模板参数
- 从类C++外部调用指向成员方法的成员指针
- 在另一个 QThread 上运行成员方法时,无法将事件发送到其他线程拥有的对象
- 对成员类型的成员方法使用 std::result_of<>
- 我是否应该在构造函数或成员方法中进行大量初始化工作
- 如何检查是否调用了成员方法
- 我怎么可能从尚未定义某些成员方法的类创建实例?(C++)
- 有没有办法强制对 clang-format/clang-tidy 中的类成员/方法使用 "this->"?
- 指向 objc 类成员方法的函数指针,用于C++
- 对集合调用成员方法
- JetBrains CLion 不能建议字符串的成员方法
- 我无法在班级中获得MED股票来调用成员方法
- 如何在本机回调中使用Cython CDEF类成员方法
- 通过指针从for_each循环中访问成员方法
- 调用成员方法时会丢失此指针
- 为什么点运算符(.)可以与私有类成员/方法一起使用
- 静态调度以使用聚合类型的层次结构更正成员方法
- 显式模板专用化不能具有存储类 - 成员方法专用化