C/C++ 定义 “IDisposable”,用于管理被实例化的对象持有资源的释放是有必要的一种设计模式,很多人习惯使用析构来释放,但这只适用于不涉及虚函数的情况。
C/C++ 结构/类型析构函数及构造函数不可以调用虚函数,在不同的编译器上有不同的表现,如函数被定义虚函数且为抽象(Abstract)那么析构函数内调用会崩溃,C# 语言没有真正意义上与C/C++相同的析构函数,在C#语言中析构函数可以调用虚函数,这是因为.NET虚拟机在调用析构函数时采用的办法是类似用户调用 “Dispose()” 虚函数(il.callvirt)的行为,此时并没有释放引用对象上面的虚表指针,而在 C/C++ 中析构会破坏当前的虚表指针,所以C/C++析构函数内无法连接到虚函数,但并非绝对的,GCC/VC++ 编译器允许析构内连接虚函数,但仅限于当前结构/类型的成员函数,而重载虚表的虚函数不能被调用。
C/C++ 应用程序一个好的设计及编码,应该避免过度依赖析构函数释放资源,这容易引发一些不必要的内存安全问题。
所有的对象持有的托管/非托管资源及内存都应该由对象派生接口 “IDisposable” 的 “Dispose()” 具现函数处理,C/C++ 析构函数仅用于 STL/Boost 类型成员编译器自行析构或逃逸的非托管资源释放。
注意:
托管资源一般泛指由对象本身持有管理的数据,它可以是其它对象的共享/唯一引用,简单理解,可以是认为 std::shared_ptr、boost::shared_ptr 指向的类型对象实例,非托管资源,可以理解为 “操作系统句柄”、“硬件物理/虚拟内存资源”。
应用代码:
std::shared_ptr<User> user = Reference::NewReference<User>();
uusing(user);
实现代码:
class Reference : public std::enable_shared_from_this<Reference> {
public:
inline Reference() noexcept
: enable_shared_from_this() {
}
public:
inline std::shared_ptr<Reference> GetReference() const noexcept {
return const_cast<Reference*>(this)->shared_from_this();
}
public:
template<typename _Ty1, typename _Ty2>
inline static std::shared_ptr<_Ty1> AsReference(const std::shared_ptr<_Ty2>& v) noexcept {
return v ? std::dynamic_pointer_cast<_Ty1>(v) : NULL;
}
public:
template<typename _Ty1, typename _Ty2>
inline static std::shared_ptr<_Ty1> CastReference(const std::shared_ptr<_Ty2>& v) noexcept {
if (!v) {
return NULL;
}
_Ty2* native_pTy2 = const_cast<_Ty2*>(v.get());
_Ty1* native_pTy1 = static_cast<_Ty1*>(native_pTy2);
const std::shared_ptr<_Ty2> shared_pTy2 = v;
return std::shared_ptr<_Ty1>(native_pTy1, [shared_pTy2](const void*) noexcept {});
}
public:
template<class _Ty1 = Reference, class _Ty2 = Reference, typename... A>
inline static std::shared_ptr<_Ty1> NewReference2(A&&... args) noexcept {
static_assert(sizeof(_Ty1) > 0 && sizeof(_Ty2) > 0, "can't make pointer to incomplete type");
_Ty2* p = (_Ty2*)Malloc(sizeof(_Ty2));
return std::shared_ptr<_Ty1>(new (p) _Ty2(std::forward<A&&>(args)...),
[](_Ty2* p) noexcept {
p->~_Ty2();
Mfree(p);
});
}
template<class _Ty1 = Reference, typename... A>
inline static std::shared_ptr<_Ty1> NewReference(A&&... args) noexcept {
return NewReference2<_Ty1, _Ty1>(std::forward<A&&>(args)...);
}
};
class IDisposable : public Reference {
public:
virtual void Dispose() = 0;
};
class User : public IDisposable {
virtual void Dispose() override {};
};
void uusing(const IDisposable* disposable) {
if (disposable) {
const_cast<IDisposable*>(disposable)->Dispose();
}
};
template<typename Reference>
void uusing(const std::shared_ptr<Reference>& reference) {
if (reference) {
uusing(reference.get());
}
}
template<typename Reference>
void uusing(const std::unique_ptr<Reference>& reference) {
if (reference) {
uusing(reference.get());
}
}