我可以理解boost::shared_ptr
在调用自定义删除函数之前不会验证NULL
,但我怎样才能实现这一点?这将帮助我避免为fclose
或任何没有(正确)指定行为的函数编写哑包装器。
我的提升:#define BOOST_VERSION 104500
。这不是C ++ 11(因为我使用了boost)。
问题与:make shared_ptr not use delete
有关示例代码:
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
的第二部分:为什么将删除器作为函数参数而不是模板参数?显然,答案就在这里:Why does unique_ptr take two template parameters when shared_ptr only takes one? C ++ 11答案是unique_ptr,但是为什么boost没有提供 - 我们永远不会知道。
答案 0 :(得分:1)
似乎是empty
(非拥有)shared_ptr和null
之间的区别。
Nulled将会(并且应该)调用删除器(它可能意味着特定类型的资源句柄。实际上,值&#34; 0&#34;可能甚至不是特殊的。)
空的将调用删除器,但带有nullptr_t
参数。因此,您通常可以创建一个包装任何adhoc删除器的包装器(无论是内联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 << "Deleting\n";});
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
看起来你非常&#34; 纯&#34;,忠实地将原始意图传递给底层API。这是一个GoodThing™,因为如果shared_ptr<>
做出了#34;任意&#34;例外情况,在删除&#34; NULL&#34; (或默认值)值是有意义的,不应跳过。
现在,选择是你的,应该是。
当然,如果资源句柄值是&#34; NULL&#34;那么你可以使用非常类似的包装策略,就像我刚刚展示的那样,通常会跳过底层的API调用。或者无效。
<强> Live On Coliru 强>
#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 deleting\n"; }
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 << "Deleting\n";});
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
}
答案 1 :(得分:1)
有一种想法可能不是试图保护析构函数,而是应该强制构造失败,从而避免在破坏期间检查无效。
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
,如果由于某种原因仍然会发生这种情况。
与make shared_ptr not use delete不同 我认为由于功能的性质,你只是坚持使用这些较长的包装器。