为什么在公共可访问的头文件中描述了私有变量?


11

好的,因此希望对程序员来说这是一个主观的问题,但这是可行的。我一直在不断拓宽我的语言和软件工程实践的知识……而且我遇到了一些对我毫无意义的事情。

在C ++中,类声明将private:方法和参数包含在头文件中,从理论上讲,如果将它们设为lib,则传递给用户以包括这些内容。

在Objective-C中,@interface几乎做同样的事情,迫使您列出私有成员(至少有一种方法可以在实现文件中获取私有方法)。

据我所知,Java和C#允许您提供一个接口/协议,该接口/协议可以声明所有可公开访问的属性/方法,并使编码人员能够在实现文件中隐藏所有实现细节。

为什么?封装是OOP的主要原理之一,为什么C ++和Obj-C缺少此基本功能?对于Obj-C或C ++,是否存在某种隐藏所有实现的最佳实践解决方法?

谢谢,


4
我感到你很痛苦。第一次将私有字段添加到类中并且不得不重新编译使用它的所有内容时,我逃避了C ++的尖叫。
拉里·科尔曼

@Larry,我不太介意它,但是它似乎被誉为一种很棒的OO语言,但它甚至不能“适当地”封装。
Stephen Furlani 2010年

2
不要相信炒作。在静态和动态打字营中,都有更好的OO方法。
拉里·科尔曼

这是一个在某些方面有很大的语言,但不是因为它的OO能力,这是西穆拉67的接枝到C的
大卫·索恩利

您应该进行一些C编程。然后,你将有一个更好的理解为什么这些语言会是这样的,包括Java C#等
亨利

Answers:


6

问题是编译器是否需要知道对象的大小。如果是这样,则编译器必须知道私有成员,以便对其进行计数。

在Java中,有原始类型和对象,并且所有对象都是单独分配的,包含它们的变量实际上是指针。因此,由于指针是固定大小的对象,因此编译器知道变量代表的大小,而无需知道指向对象的实际大小。构造函数处理所有这些。

在C ++中,可以在本地或堆上表示对象。因此,编译器需要知道一个对象有多大,以便它可以分配局部变量或数组。

有时需要将类功能划分为公共接口和私有的所有其他东西,这就是PIMPL(实现指针)技术的用处。该类将有一个指向私有实现类的指针,公共类方法可以调用那。


编译器无法通过对象库获取此信息吗?为什么这些信息不是机器可读的而是人类可读的?
Stephen Furlani 2010年

@Stephen:在编译时,不一定有一个对象库。由于对象的大小会影响编译后的代码,因此必须在编译时而不是仅在链接时就知道它。此外,Ah可以定义A类,Bh可以定义B类,而A.cpp中的函数定义可以使用B类的对象,反之亦然。在这种情况下,如果从目标代码中获取目标大小,则必须先编译A.cpp和B.cpp中的每一个。
David Thornley 2010年

编译期间无法链接?我承认不了解该深度的详细信息,但是如果对象的接口公开了其内容,那么是否可以从库或其他机器可读组件中公开这些相同的内容? 必须在可公开访问的头文件中定义它们吗?我理解,这是大多数同C语言中的一部分,但它必须是这样的吗?
Stephen Furlani 2010年

1
@Stephen:只有在所有适当的组件都存在的情况下,才能在编译期间完成链接。即使那样,这仍将是一个复杂的过程,涉及部分编译(除对象大小以外的所有内容),部分链接(以获取对象大小),最终编译和链接。鉴于C和C ++是相对古老的语言,这根本不会发生。大概有人可以设计没有头文件的新语言,但是不是C ++。
David Thornley 2010年

14

由于C ++的设计,为了在堆栈上创建对象,编译器必须知道它有多大。为此,它需要在头文件中包含所有字段,因为这是编译器在包含头时可以看到的全部内容。

例如,如果您定义一个类

class Foo {
    public int a;
    private int b;
};

然后sizeof(Foo)sizeof(a) + sizeof(b)。如果有某种机制可以分隔私有字段,那么标头可能包含

class Foo {
    public int a;
};

sizeof(Foo) = sizeof(a) + ???

如果您想真正隐藏私人数据,请尝试使用pimpl习惯用法,

class FooImpl;
class Foo {
    private FooImpl* impl;
}

在标头中,FooImpl仅在Foo的实现文件中定义。


哦。我不知道是这种情况。这就是为什么Java,C#和Obj-C之类的语言具有“类对象”,以便他们可以询问类对象应该有多大?
Stephen Furlani 2010年

4
尝试使用pimpl(指向私有实现的指针)。由于所有类指针的大小相同,因此您不必定义实现类,只需声明它即可。
斯科特·威尔士

我认为这应该是答案,而不是评论。
拉里·科尔曼

1

所有这些都取决于设计选择。

如果您确实希望隐藏C ++或Objective-C类的私有实现细节,则可以提供该类支持的一个或多个接口(C ++纯虚拟类,Objective-C @protocol)和/或创建该类可以通过提供静态工厂方法或类工厂对象来构造自身。

私有变量在头文件/类声明/ @接口中公开的原因是,类的使用者可能需要创建它的新实例,而客户端代码中的new MyClass()[[MyClass alloc]init]则需要编译器了解MyClass的大小。对象是为了做分配。

Java和C#在类中还详细说明了它们的私有变量-它们也不例外,但是IMO接口范式在这些语言中更为常见。您可能没有每种情况的源代码,但是编译后的/字节代码中有足够的元数据可以推断出此信息。由于C ++和Objective-C没有此元数据,因此唯一的选择是class / @ interface的实际详细信息。在C ++ COM世界中,您不公开任何类的私有变量,但可以提供头文件-因为该类是纯虚拟的。一个类工厂对象也被注册以创建实际的实例,并且存在各种形式的元数据。

在C ++和Objective-C中,分发头文件要比编写和维护附加的interface / @ protocol文件少。这是您经常看到私有实现的原因之一。

C ++中的另一个原因是模板–编译器需要了解类的详细信息,以便生成专门针对所提供参数的该类的版本。类成员的大小将随参数设置而变化,因此它需要此信息。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.