filesystem::path
Filesystem 库在表示抽象路径名(甚至不是存在的文件名)的类型和访问实际物理文件系统的操作(即在磁盘上读+写数据)之间有非常明确的分离.
您甚至指出了对此的解释:
设计规则是纯词法操作作为类路径成员函数提供,而操作系统执行的操作作为自由函数提供。
这就是原因。
理论上可以filesystem::path
在没有磁盘的系统上使用 a 。该类path
只保存一个字符串,并允许操作该字符串、在字符集之间进行转换以及使用一些规则来定义主机操作系统上的文件名和路径名的结构。例如,它知道目录名称/
在 POSIX 系统和\
Windows 上是由分隔的。操作 a 中保存的字符串path
是一种“词法操作”,因为它只是执行字符串操作。
被称为“文件系统操作”的非成员函数是完全不同的。它们不仅仅使用path
只是一串字符的抽象对象,它们执行访问文件系统的实际 I/O 操作(stat
系统调用open
等readdir
)。这些操作接受一个path
参数来命名要操作的文件或目录,然后它们访问真实的文件或目录。他们不只是操纵内存中的字符串。
这些操作依赖于操作系统提供的用于访问文件的 API,并且它们依赖于硬件,这些硬件可能以与内存中字符串操作完全不同的方式失败。磁盘可能已满,或者可能在操作完成之前被拔掉,或者可能有硬件故障。
这么看,当然file_size
不是 的成员path
,因为它与路径本身无关。路径只是文件名的表示,而不是实际文件的表示。该函数file_size
查找具有给定名称的物理文件并尝试读取其大小。这不是文件名的属性,而是文件系统上持久文件的属性。与内存中保存文件名的字符串完全分开存在的东西。
换句话说,我可以拥有一个path
包含完全废话的对象,就像filesystem::path p("hgkugkkgkuegakugnkunfkw")
这很好。我可以附加到那个路径,或者询问它是否有根目录等。但如果它不存在,我就无法读取这样的文件的大小。我可以有一个确实存在的文件的路径,但我没有访问权限,就像filesystem::path p("/root/secret_admin_files.txt");
这也很好,因为它只是一个字符串。当我尝试使用文件系统操作函数访问该位置的某些内容时,我只会收到“权限被拒绝”错误。
因为path
成员函数从不接触文件系统,所以它们永远不会由于权限或不存在的文件而失败。这是一个有用的保证。
您可以使用迭代器观察到类似的模式,现在我们可以(应该?)做 begin(it) 而不是 it.begin(),但在这里我认为基本原理是更符合非修改 next (它)等等。
不,这是因为它同样适用于数组(不能有成员函数)和类类型。如果您知道您正在处理的类似范围的东西是容器而不是数组,那么您可以使用x.begin()
,但是如果您正在编写通用代码并且不知道它是容器还是数组,那么std::begin(x)
在这两种情况下都可以使用。
这两件事的原因(文件系统设计和非成员范围访问功能)都不是一些反面向对象的偏好,它们是出于更明智、更实际的原因。基于它们中的任何一个都是糟糕的设计,因为它对一些喜欢 OO 的人感觉更好,或者对不喜欢 OO 的人感觉更好。
此外,当一切都是成员函数时,有些事情你不能做:
struct ConvertibleToPath {
operator const std::filesystem::path& () const;
// ...
};
ConvertibleToPath c;
auto n = std::filesystem::file_size(c); // works fine
但如果file_size
是以下成员path
:
c.file_size(); // wouldn't work
static_cast<const std::filesystem::path&>(c).file_size(); // yay, feels object-ish!