C++模块标准是否解决了隐藏私有数据不让调用者访问的问题?

5
在C++中,标准化模块已被引入以解决#include过多等问题。C++编译器需要解析太多信息。
此外,由于C++内联存储数据非常高效,即使调用者也必须了解对象的内存布局。
即将推出的模块标准是否解决了这个问题?
示例:
class GLWin {
private:
  GLFWwindow* win;
  glm::mat4 projection;
  ...
};

一个包含指向内部实现的指针的对象可以通过空声明来解耦,即:

class GLFWwindow;

但是,如果出于性能考虑,我们将mat4对象包含在窗口中,那么我们需要知道大小,这意味着必须包含定义,带入一个通常因为级联包含而非常庞大的头文件。模块中是否有任何机制可以隐藏细节并允许保留正确数量的对象空间,同时像指针一样保持不透明?

2个回答

9

模块化并不能使系统实现起来代码对于模块外的类型私有成员一无所知。这与允许查询和迭代类型的私有成员的静态反射提议是不兼容的。

模块化所做的是:

  1. 当您遇到这些递归的“包含”时,它们实际上并没有将这些内部内容暴露给外部代码。以您的示例为例,假设glm :: mat4来自名为GLM的模块。声明GLFWin的模块会有import GLM,因为它需要这些定义来工作。但是,这是一个实现细节,因此您不会执行 export import GLM

    现在,其他人来导入您的模块。为了执行该导入,编译器将必须读取GLM模块。但是,由于您的模块未导出GLM,因此导入您的模块的代码无法使用它。也就是说,他们自己不能使用glm :: mat4或任何其他东西,除非他们自己导入该模块。

    这似乎没有什么区别,因为仍然需要GLM模块,但这是一个重大的区别。用户不会因为使用一个模块而获得界面,只有因为他们正在使用另一个模块。

  2. 这些导入并不痛苦。编译模块的结果应该是一个文件(通常称为BMI,“二进制模块接口”),这是编译器可以快速读取并转换为其内部数据结构的东西。此外,如果在同一编译器进程中编译多个翻译单元,则它们可以共享已加载的模块。毕竟,GLM不会因导入它的位置而改变,因此没有理由重新加载模块;只需使用内存中已有的内容即可。

    最后,还有重新编译。如果您使用标头文件,并更改了GLM标头文件,则需要重新编译包含它们的每个文件。虽然在模块中也是如此,但方式远远不那么痛苦。

    假设创建GLFWin的模块和消耗它的模块都在某个时候使用到std :: vector 。现在,假设您更改了GLM,因此必须重新编译两个模块。在标头世界中,这意味着两个文件必须重新编译<vector>标头文件,即使它未更改且不依赖于GLM。这就是文本包含的工作原理。

    在模块化世界中,它们不必重新编译vector模块。它与GLM模块没有任何依赖关系,因此只需使用已经存在的vector模块即可。对于任何不依赖于GLM的包含模块都是如此。因此,虽然仍需要重新编译级联,但由于不必重新编译每个翻译单元本身使用的所有内容,因此重新编译本身应该明显更快。一个5000行的文件就像一个5000行的文件重新编译,而不是5000加上它所包含的行数。


-1
模块概念改变了我们对依赖关系的思考方式。不再有头文件,而是二进制模块接口(BMI),由编译器生成并包含有关对象大小、对象结构和依赖关系的所有信息。您的类的模块必须依赖于GLFWindow和glm::mat的模块,否则无法编译它。因此,在某种意义上,您仍然需要向其他类公开内部数据,但是您的编译器不必爬行所有包含文件,而只需在BMI的导入上进行解析,这些导入是需要理解类/函数接口的,如果它发现多次作为依赖项的相同BMI,则只会解析一次。
这也意味着,您将不再将定义和声明分别放在不同的文件中,因为这没有任何意义。您最终将得到类似Java .class文件的东西。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接