为什么C ++采用免费函数:
std::make_unique(...);
std::make_shared(...);
而不是使用静态成员函数:
std::unique_ptr::make(...); // static
std::shared_ptr::make(...); // static
答案 0 :(得分:24)
TL; DR:静态成员函数始终可以访问私有数据,但是当明确标记为friend
时,自由函数只能访问私有数据。将这些函数实现为自由函数(少数被实现为友元函数)的选择不是随机历史工件,而是在为所有{{1}具有一致命名方案的同时改进封装的有意决定。函数。
C ++中有many standard factory functions:
std::make_x
对于上述所有内容,只能使用std::make_pair
std::make_tuple
std::make_unique
std::make_shared //efficiency
std::make_exception_ptr //efficiency
std::make_move_iterator
std::make_reverse_iterator
std::make_error_code
std::make_error_condition
//And several more are proposed for C++17
的公共接口正确实现make_x
功能。对于x
和make_shared
,最有效的实施方式需要访问make_exception_ptr
或std::shared_ptr
的内部数据。所有其他功能都可以使用公共接口实现,而且性能损失为零。
将这些函数实现为非朋友自由函数可以减少访问对象私有内部的代码量(a desirable property),因为当较少的代码可以访问私有数据时,可以减少访问私有数据的位置。必须对违反对象不变量的操作进行审计,如果对象内部发生更改,则可能需要更改的位置更少。
如果std::exception_ptr
是唯一类似的工厂函数,它可能是成员函数,但由于大多数此类函数不需要make_shared
函数来有效运行,为了保持一致性,friend
也作为自由函数实现。
这是正确的设计,就像一直使用静态成员make_shared
函数一样,然后在除make
和make_shared
之外的每种情况下,成员函数都不可避免地会有过多的访问权限。 make_exception_ptr
对象的私有数据。使用标准化设计,需要访问私有数据的少量x
函数可以标记为make_x
,其余部分默认正确封装。如果在某些情况下使用非成员friend
而在其他情况下使用静态成员make_x
,则标准库将变得不一致且更难学习。
答案 1 :(得分:17)
<强>一致性强>
我不认为有任何令人信服的理由使用::make
语法而不是当前语法。我假设make_unique
和make_shared
首选静态::make
函数,以保持与现有的std::make_pair
和std::make_heap
函数一致,这些函数存在于C +之前11。
请注意std::make_pair
有一个很大的优势:它会自动从函数调用中推导出结果对的类型:
auto p0 = std::make_pair(1, 1.f); // pair<int, float>
如果我们有std::pair::make
,那么我们必须写:
auto p1 = std::pair<int, float>::make(1, 1.f);
这违背了make_pair
的目的。
因此,我认为选择make_unique
和make_shared
是因为开发人员已经习惯make_pair
和类似的功能。
make_pair
被选中而不是pair::make
以获得上述好处。
答案 2 :(得分:3)
除了惯例之外,没有具体的理由 - 静态类函数可以完成全局函数可以执行的所有操作(功能明智) C ++更喜欢包含在已定义的命名空间内的全局函数(用于实用程序函数) 其他编程语言(如Java)更喜欢静态公共函数,因为不支持全局函数。
这对make_***
来说并不陌生,其他例子存在:
std::this_thread::XXXX
而不是std::thread::XXXX_current
尽管将与当前执行线程相关的函数作为静态函数放在thread
类中可能有意义,但它们是全局的在this_thread
命名空间内。
另外,我们可以使用std::container::sort
这样的std::container
是容器的辅助类,但我们改为std::sort
。