我可以理解boost::shared_ptr
在调用自定义删除器函数之前不会验证NULL
,但是我该如何实现呢?这将帮助我避免为fclose
或任何没有(正确)指定行为的函数编写愚蠢的包装器。
我的助推器:#define BOOST_VERSION 104500
。这不是 11 C++(因为我使用 boost)。
该问题与:使shared_ptr不使用删除
有关示例代码:
static inline
FILE* safe_fopen(const char* filename, const char* mode)
{
FILE* file = NULL;
(void)fopen_s(&file, filename, mode);
return file;
}
static inline
void safe_fclose(FILE* file)
{
if (file)
BOOST_VERIFY(0 == fclose(file));
}
...
boost::shared_ptr<FILE> file( safe_fopen(FILE_DOWNLOAD, "rb"), safe_fclose);
...
// now it is created => open it once again
file.reset( safe_fopen(FILE_DOWNLOAD, "rb"), safe_fclose);
编辑
我的问题最初是关于使用shared_ptr
的第二部分:为什么提供删除器作为函数参数而不是模板参数?显然,答案就在这里:为什么unique_ptr采用两个模板参数,而shared_ptr只采用一个模板参数?C++ 11 的答案是unique_ptr,但为什么 boost 没有提供一个 - 我们永远不会知道。
这似乎是关于empty
(非拥有)shared_ptr和null
-ed之间的区别。
空的将(并且应该)调用删除器(对于特定类型的资源句柄,这可能意味着某些内容。事实上,值"0"甚至可能并不特别)。
空的将调用删除器,但带有nullptr_t
参数。因此,您通常可以制作一个包装器来包装任何临时删除器(无论是内联 lambda 还是像 &::free
这样的函数指针):
template <typename F>
OptionalDeleter<F> make_optional_deleter(F&& f) { return OptionalDeleter<F>(std::forward<F>(f)); }
int main() {
auto d = make_optional_deleter([](void*){std::cout << "Deletingn";});
using namespace std;
{
shared_ptr<int> empty(std::nullptr_t{}, d);
} // deleter not called, empty
{
shared_ptr<int> thing(static_cast<int*>(nullptr), d);
} // deleter called, thing not empty
}
将打印
Empty, not deleting
Deleting
看起来你得到了非常"纯粹"的、忠实的、将原始意图传递给底层 API。这是一个好事™,因为如果shared_ptr<>
"任意"例外,那么在删除"NULL"(或默认值)值有意义且不应跳过的情况下,它将变得不可用。
现在,选择权在你手中,理应如此。
当然,您可以使用非常相似的包装策略,就像我刚刚展示的那样,如果资源句柄值为"NULL"或其他无效,则通常跳过底层 API 调用。
住在科里鲁
#include <memory>
#include <iostream>
template<typename F>
struct OptionalDeleter final {
explicit OptionalDeleter(F&& f) : _f(std::forward<F>(f)) {}
template <typename... Ts>
void operator()(Ts&&... args) const { _f(std::forward<Ts>(args)...); }
void operator()(std::nullptr_t) const { std::cout << "Empty, not deletingn"; }
private:
F _f;
};
template <typename F>
OptionalDeleter<F> make_optional_deleter(F&& f) { return OptionalDeleter<F>(std::forward<F>(f)); }
int main() {
auto d = make_optional_deleter([](void*){std::cout << "Deletingn";});
using namespace std;
{
shared_ptr<int> empty(std::nullptr_t{}, d);
} // deleter not called, empty
{
shared_ptr<int> thing(static_cast<int*>(nullptr), d);
} // deleter called, thing not empty
}
一个想法可能是,与其试图保护析构函数,不如在构造时强制失败,从而避免在销毁过程中检查无效。
static inline
FILE* safe_fopen(const char* filename, const char* mode)
{
FILE* file = NULL;
(void)fopen_s(&file, filename, mode);
if (!file)
throw std::exception(...); // check errno
return file;
}
但是,如果由于某种原因仍然发生未初始化的boost::shared_ptr
,这无助于修复。
与不使用删除不同shared_ptr我认为由于函数的性质,您只是坚持使用这些更长的包装器。